diff --git a/.github/workflows/backend.yml b/.github/workflows/backend.yml
deleted file mode 100644
index 0dbde1e85f6f92ad897d9e98d9fa306c4d127e76..0000000000000000000000000000000000000000
--- a/.github/workflows/backend.yml
+++ /dev/null
@@ -1,28 +0,0 @@
-name: Backend tests
-
-on: [ push ]
-
-env:
-  NODE_ENV: test
-
-jobs:
-  backend:
-    if: "!contains(github.event.head_commit.message, '[skip ci]')"
-    runs-on: ubuntu-latest
-    strategy:
-      matrix:
-        node-version: [10.x, 12.x]
-    steps:
-    - uses: actions/checkout@v1
-    - name: Use Node.js ${{ matrix.node-version }}
-      uses: actions/setup-node@v1
-      with:
-        node-version: ${{ matrix.node-version }}
-    - name: cd into deploy, npm install
-      run: |
-        cd deploy
-        npm i
-    - name: test
-      run: |
-        cd deploy
-        npm run test
\ No newline at end of file
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..fc7f269f857c208f90c296c969cd5925b22bb6a3
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,69 @@
+name: '[ci]'
+
+on:
+  push:
+    branches-ignore:
+    - 'dev'
+    - 'staging'
+    - 'master'
+
+    # ignore changes to docs and mkdocs.yml
+    paths-ignore:
+    - 'docs/**'
+    - 'mkdocs.yml'
+
+jobs:
+  lint:
+    if: always()
+    runs-on: ubuntu-latest
+
+    steps:
+    - uses: actions/checkout@v2
+    - name: Use Node.js 14.x for lint
+      uses: actions/setup-node@v1
+      with:
+        node-version: '14.x'
+    - run: npm i
+    - run: npm run lint
+
+  frontend:
+    if: always()
+    runs-on: ubuntu-latest
+
+    strategy:
+      matrix:
+        node-version: [10.x, 12.x, 14.x]
+
+    env:
+      NODE_ENV: test
+      
+    steps:
+    - uses: actions/checkout@v2
+    - name: Use Node.js ${{ matrix.node-version }}
+      uses: actions/setup-node@v1
+      with:
+        node-version: ${{ matrix.node-version }}
+    - run: npm i
+    - run: npm run test
+
+  backend:
+    if: always()
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        node-version: [10.x, 12.x, 14.x]
+
+    env:
+      NODE_ENV: test
+
+    steps:
+    - uses: actions/checkout@v2
+    - name: Use Node.js ${{ matrix.node-version }}
+      uses: actions/setup-node@v1
+      with:
+        node-version: ${{ matrix.node-version }}
+    - run: |
+        cd deploy
+        npm i
+        npm run test
+  
\ No newline at end of file
diff --git a/.github/workflows/docker_img.yml b/.github/workflows/docker_img.yml
new file mode 100644
index 0000000000000000000000000000000000000000..7e8ad0953d6b26a262965793b2a0ee3002a32b0d
--- /dev/null
+++ b/.github/workflows/docker_img.yml
@@ -0,0 +1,167 @@
+name: '[docker image]'
+
+on: [ 'push' ]
+
+jobs:
+  build-docker-img:
+    runs-on: ubuntu-latest
+    env:
+      MATOMO_ID_DEV: '7'
+      MATOMO_URL_DEV: 'https://stats-dev.humanbrainproject.eu/'
+      MATOMO_ID_PROD: '12'
+      MATOMO_URL_PROD: 'https://stats.humanbrainproject.eu/'
+      PRODUCTION: 'true'
+      DOCKER_REGISTRY: 'docker-registry.ebrains.eu/siibra/'
+      SIIBRA_API_STABLE: 'https://siibra-api-stable.apps.hbp.eu/v1_0'
+      SIIBRA_API_RC: 'https://siibra-api-rc.apps.hbp.eu/v1_0'
+      SIIBRA_API_LATEST: 'https://siibra-api-latest.apps-dev.hbp.eu/v1_0'
+
+    steps:
+    - uses: actions/checkout@v2
+    - name: 'Set matomo env var'
+      run: |
+        echo "Using github.ref: $GITHUB_REF"
+
+        echo "BRANCH_NAME=${GITHUB_REF#refs/heads/}" >> $GITHUB_ENV
+        
+        if [[ "$GITHUB_REF" == 'refs/heads/master' ]]
+        then
+          echo "Either master, using prod env..."
+          echo "MATOMO_URL=${{ env.MATOMO_URL_PROD }}" >> $GITHUB_ENV
+          echo "MATOMO_ID=${{ env.MATOMO_ID_PROD }}" >> $GITHUB_ENV
+          echo "BS_REST_URL=${{ env.SIIBRA_API_STABLE }}" >> $GITHUB_ENV
+        elif [[ "$GITHUB_REF" == 'refs/heads/staging' ]]
+        then
+          echo "Either staging, using staging env..."
+          echo "MATOMO_URL=${{ env.MATOMO_URL_PROD }}" >> $GITHUB_ENV
+          echo "MATOMO_ID=${{ env.MATOMO_ID_PROD }}" >> $GITHUB_ENV
+          echo "BS_REST_URL=${{ env.SIIBRA_API_RC }}" >> $GITHUB_ENV
+        else
+          echo "Using dev env..."
+          echo "MATOMO_URL=${{ env.MATOMO_URL_DEV }}" >> $GITHUB_ENV
+          echo "MATOMO_ID=${{ env.MATOMO_ID_DEV }}" >> $GITHUB_ENV
+          echo "BS_REST_URL=${{ env.SIIBRA_API_LATEST }}" >> $GITHUB_ENV
+        fi
+
+    - name: 'Set version variable'
+      run: |
+        if [[ "$GITHUB_REF" == 'refs/heads/master' ]] || [[ "$GITHUB_REF" == 'refs/heads/staging' ]]
+        then
+          echo "Either master or staging, using package.json"
+          VERSION=$(jq -r '.version' package.json)
+        else
+          echo "Using git hash"
+          VERSION=$(git rev-parse --short HEAD)
+        fi
+        echo "VERSION=$VERSION" >> $GITHUB_ENV
+    - name: 'Build docker image'
+      run: |
+        DOCKER_BUILT_TAG=${{ env.DOCKER_REGISTRY }}siibra-explorer:$BRANCH_NAME
+        echo "Building $DOCKER_BUILT_TAG"
+        docker build \
+          --build-arg VERSION=$VERSION \
+          --build-arg MATOMO_URL=$MATOMO_URL \
+          --build-arg MATOMO_ID=$MATOMO_ID \
+          --build-arg BS_REST_URL=$BS_REST_URL \
+          -t $DOCKER_BUILT_TAG \
+          .
+        echo "Successfully built $DOCKER_BUILT_TAG"
+        echo "DOCKER_BUILT_TAG=$DOCKER_BUILT_TAG" >> $GITHUB_ENV
+
+    - name: 'Push to docker registry'
+      run: |
+        echo "Login to docker registry"
+        docker login \
+          -u '${{ secrets.EBRAINS_DOCKER_REG_USER }}' \
+          -p '${{ secrets.EBRAINS_DOCKER_REG_TOKEN }}' \
+          docker-registry.ebrains.eu
+        echo "Pushing $DOCKER_BUILT_TAG"
+        docker push $DOCKER_BUILT_TAG
+
+  trigger-deploy:
+    if: success()
+    runs-on: ubuntu-latest
+    env:
+      GITHUB_API_ROOT: https://api.github.com/repos/fzj-inm1-bda/siibra-explorer
+      
+    needs: build-docker-img
+    steps:
+      - uses: actions/checkout@v2
+      - name: Set env var
+        run: |
+          echo "Using github.ref: $GITHUB_REF"
+          BRANCH_NAME=${GITHUB_REF#refs/heads/}
+          echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV
+        
+          echo "Branch is $BRANCH_NAME ."
+
+          if [[ "$BRANCH_NAME" == 'master' ]]
+          then
+            echo "BUILD_TEXT=" >> $GITHUB_ENV
+          else
+            echo "BUILD_TEXT=$BRANCH_NAME" >> $GITHUB_ENV
+          fi
+
+          if [[ "$BRANCH_NAME" == 'master' ]] || [[ "$BRANCH_NAME" == 'staging' ]]
+          then
+            echo "OKD_URL=https://okd.hbp.eu:443" >> $GITHUB_ENV
+            echo "OKD_SECRET=${{ secrets.OKD_PROD_SECRET }}" >> $GITHUB_ENV
+            echo "OKD_PROJECT=interactive-viewer" >> $GITHUB_ENV
+            echo "PATH_POSTFIX=" >> $GITHUB_ENV
+            echo "Deploy on prod cluster..."
+          else
+            echo "OKD_URL=https://okd-dev.hbp.eu:443" >> $GITHUB_ENV
+            echo "OKD_SECRET=${{ secrets.OKD_DEV_SECRET }}" >> $GITHUB_ENV
+            echo "OKD_PROJECT=interactive-atlas-viewer" >> $GITHUB_ENV
+            echo "PATH_POSTFIX=-dev" >> $GITHUB_ENV
+            echo "BUILD_TEXT=$BRANCH_NAME" >> $GITHUB_ENV
+            echo "Deploy on dev cluster..."
+          fi
+      - name: 'Login via oc cli & deploy'
+        run: |
+          oc login $OKD_URL --token=$OKD_SECRET
+          oc project $OKD_PROJECT
+
+          # sanitized branchname == remove _ / and lowercase everything
+          SANITIZED_BRANCH_NAME=$(echo ${BRANCH_NAME//[_\/]/} | awk '{ print tolower($0) }')
+          echo "SANITIZED_BRANCH_NAME=$SANITIZED_BRANCH_NAME" >> $GITHUB_ENV
+          echo "Working branch name: $BRANCH_NAME, sanitized branch name: $SANITIZED_BRANCH_NAME"
+
+          # check if the deploy already exist
+          if oc get dc siibra-explorer-branch-deploy-$SANITIZED_BRANCH_NAME; then
+            # trigger redeploy if deployconfig exists already
+            echo "dc siibra-explorer-branch-deploy-$SANITIZED_BRANCH_NAME already exist, redeploy..."
+            oc rollout latest dc/siibra-explorer-branch-deploy-$SANITIZED_BRANCH_NAME
+          else 
+            # create new app if deployconfig does not yet exist
+            echo "dc siibra-explorer-branch-deploy-$SANITIZED_BRANCH_NAME does not yet exist, create new app..."
+            oc new-app --template siibra-explorer-branch-deploy \
+              -p BRANCH_NAME=$BRANCH_NAME \
+              -p SANITIZED_BRANCH_NAME=$SANITIZED_BRANCH_NAME \
+              -p PATH_POSTFIX=$PATH_POSTFIX \
+              -p BUILD_TEXT=$BUILD_TEXT
+          fi
+      - name: 'Update status badge'
+        if: success()
+        run: |
+
+          if [[ "$GITHUB_REF" == 'refs/heads/master' ]]
+          then
+            DEPLOY_URL="https://siibra-explorer.apps.hbp.eu/master"
+          elif [[ "$GITHUB_REF" == 'refs/heads/staging' ]]
+          then
+            DEPLOY_URL="https://siibra-explorer.apps.hbp.eu/staging"
+          else
+            DEPLOY_URL="https://siibra-explorer.apps-dev.hbp.eu/${{ env.SANITIZED_BRANCH_NAME }}"
+          fi
+
+          curl -v \
+            -X POST \
+            -H "Authorization: Token ${{ secrets.WORKFLOW_TOKEN }}" \
+            -H 'accept: application/vnd.github.v3+json' \
+            ${GITHUB_API_ROOT}/statuses/${GITHUB_SHA} \
+            -d '{
+              "target_url":"$DEPLOY_URL",
+              "name": "Deployed at OKD",
+              "state": "success"
+            }'
diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml
index 9be196849aa969eaf5cc2466f3d0bf411706dc4f..2fca59ee23190ccfaeed0a2875cef7a70a639991 100644
--- a/.github/workflows/e2e.yml
+++ b/.github/workflows/e2e.yml
@@ -1,91 +1,116 @@
-name: e2e
+name: "[e2e] prod-specs"
 
 on:
-  pull_request:
-    branches:
-      - dev
-
-env:
-  DOCKER_IMAGE_NAME: gha-iav-image
-  DOCKER_IMAGE_TAG: ${{ github.sha }}
-  DOCKER_CONTAINER_NAME: gha-iav-built-${{ github.sha }}
-  DOCKER_E2E_PPTR: gha-iav-e2e-pptr-${{ github.sha }}
-  DOCKER_E2E_NETWORK: gha-dkr-network
-  ATLAS_URL: http://gha-iav-built-${{ github.sha }}:3000/
-  CHROMIUM_VERSION: "80.0.3987.106"
-  PPTR_VERSION: "2.1.0"
+  workflow_dispatch:
+    inputs:
+      url:
+        required: true
+        default: https://interactive-viewer-next.apps-dev.hbp.eu/
+      sha:
+        required: true
+        default: ''
 
 jobs:
-  buildimage:
-    if: "!contains(github.event.head_commit.message, '[skip ci]')"
-    runs-on: self-hosted
+  update-e2e-status:
+    runs-on: ubuntu-latest
+    env:
+      URL_ROOT: https://api.github.com/repos/HumanBrainProject/interactive-viewer
+      SHA: ${{ github.event.inputs.sha }}
     steps:
-    - uses: actions/checkout@v1
-    - name: Use Node.js ${{ matrix.node-version }}
-      uses: actions/setup-node@v1
-      with:
-        node-version: ${{ matrix.node-version }}
-    - name: build docker image ${{ env.DOCKER_IMAGE_NAME }}:${{ env.DOCKER_IMAGE_TAG }}
-      run: |
-        docker build --build-arg BACKEND_URL=${BACKEND_URL} -t ${DOCKER_IMAGE_NAME}:${DOCKER_IMAGE_TAG} .
-      env:
-        BACKEND_URL: ${{ env.ATLAS_URL }}
-  test:
-    if: "!contains(github.event.head_commit.message, '[skip ci]')"
-    runs-on: self-hosted
-    needs: buildimage
+      - name: update commit status
+        run: |
+          curl -v \
+            -X POST \
+            -H "Authorization: Token ${{ secrets.WORKFLOW_TOKEN }}" \
+            -H 'accept: application/vnd.github.v3+json' \
+            ${URL_ROOT}/statuses/${SHA} \
+            -d '{
+              "target_url":"'$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID'",
+              "description": "Running e2e",
+              "state": "pending"
+            }'
+  e2e:
+    needs: update-e2e-status
+    runs-on: [self-hosted, headless]
+    strategy:
+      fail-fast: false
+      matrix:
+        protractor-spec: [
+          './src/advanced/urlParsing.prod.e2e-spec.js',
+          './src/advanced/pluginApi.prod.e2e-spec.js',
+          './src/advanced/nonAtlasImages.prod.e2e-spec.js',
+          # './src/advanced/browsingForDatasets.prod.e2e-spec.js',
+          './src/advanced/favDatasets.prod.e2e-spec.js',
+          './src/advanced/history.prod.e2e-spec.js',
+          
+          './src/selecting/region.prod.e2e-spec.js',
+          './src/selecting/template.prod.e2e-spec.js',
+          './src/selecting/atlas.prod.e2e-spec.js',
+
+          './src/layout/home.prod.e2e-spec.js'
+        ]
+    env:
+      PROTRACTOR_SPECS: ${{ matrix.protractor-spec }}
+      ATLAS_URL: ${{ github.event.inputs.url }}
+      PPTR_VERSION: "5.3.1"
+      CHROMIUM_VERSION: "86.0.4240.0"
+
+    outputs:
+      failure-state: ${{ steps.failure-state-step.outputs.failure-state }}
+
     steps:
-    - name: clean up previous 
-      run: |
-        GHA_CONTAINERS=$( docker ps | grep gha | awk '{print $1}' )
-        if [ -z "$GHA_CONTAINERS" ]; then for f in $GHA_CONTAINERS; do docker stop $f; done; fi
-    - name: run docker image ${{ env.DOCKER_IMAGE_NAME }}:${{ env.DOCKER_IMAGE_TAG }} as container ${{ env.DOCKER_CONTAINER_NAME }}
-      run: |
-        docker run \
-          --rm \
-          --name ${DOCKER_CONTAINER_NAME} \
-          --env HBP_CLIENTID=${{ secrets.HBP_CLIENTID }} \
-          --env HBP_CLIENTSECRET=${{ secrets.HBP_CLIENTSECRET }} \
-          --env REFRESH_TOKEN=${{ secrets.REFRESH_TOKEN }} \
-          --env PLUGIN_URLS=${{ env.ATLAS_URL }}/res/plugin_examples/plugin1/manifest.json \
-          -dit \
-          ${DOCKER_IMAGE_NAME}:${DOCKER_IMAGE_TAG}
-    - uses: actions/checkout@v1
-    - name: Start pptr docker container with name ${{ env.DOCKER_E2E_PPTR }}
-      run: |
-        docker run --rm \
-          -v /dev/shm:/dev/shm \
-          --name ${DOCKER_E2E_PPTR} \
-          -dt \
-          puppeteer
-        docker cp . ${DOCKER_E2E_PPTR}:/iav
-        docker exec -u root ${DOCKER_E2E_PPTR} chown -R pptruser:pptruser /iav
-        docker exec -t -w /iav ${DOCKER_E2E_PPTR} npm i
-        docker exec -t -w /iav ${DOCKER_E2E_PPTR} npm run wd -- update --versions.chrome=${{ env.CHROMIUM_VERSION }}
-        docker exec -t ${DOCKER_E2E_PPTR} npm i --no-save puppeteer@${{ env.PPTR_VERSION }}
-    - name: Setup docker network
-      run: |
-        docker network connect ${{ env.DOCKER_E2E_NETWORK }} ${{ env.DOCKER_E2E_PPTR }}
-        docker network connect ${{ env.DOCKER_E2E_NETWORK }} ${{ env.DOCKER_CONTAINER_NAME }}
-    - name: run pptr tests - ${{ env.ATLAS_URL }}
-      run: |
-        docker exec --env ATLAS_URL=${ATLAS_URL} -t -w /iav ${DOCKER_E2E_PPTR} npm run e2e
-    - name : make screenshot, if success
-      if: success()
-      run: |
-        docker exec --env ATLAS_URL=${ATLAS_URL} -t -w /iav ${DOCKER_E2E_PPTR} npm run e2e -- --specs ./e2e/screenshots/gen.js
-        docker cp ${DOCKER_E2E_PPTR}:/iav/docs/autogen_images ./autogen_images
-    - uses: actions/upload-artifact@v1
-      if: success()
+    - uses: actions/checkout@v2
       with:
-        name: screenshots-${{ github.sha }}
-        path: ./autogen_images
-    - name: cleanup, stop container ${{ env.DOCKER_CONTAINER_NAME }} && ${{ env.DOCKER_E2E_PPTR }}
-      if: success()
+        ref: ${{ github.event.ref }}
+
+    - name: Install dep
       run: |
-        docker stop ${DOCKER_CONTAINER_NAME}
-        docker stop ${DOCKER_E2E_PPTR}
-    - name: cleanup, remove image ${{ env.DOCKER_IMAGE_NAME }}:${{ env.DOCKER_IMAGE_TAG }}
-      if: success()
+        npm i
+        npm run wd -- update --versions.chrome=${CHROMIUM_VERSION}
+        npm i --no-save puppeteer@${PPTR_VERSION}
+  
+    - name: 'Run e2e for ${{ matrix.protractor-spec }} on ${{ github.event.inputs.url }}'
+      run: npm run e2e
+
+    - name: Set output when workflow fails
+      if: failure()
+      run: echo '::set-output name=failure-state::true'
+      id: failure-state-step
+
+    - name: Define screenshot artefact
+      if: failure()
       run: |
-        docker rmi ${DOCKER_IMAGE_NAME}:${DOCKER_IMAGE_TAG}
+        strip_prefix=${PROTRACTOR_SPECS#./src/}
+        strip_suffix=${strip_prefix%.e2e-spec.js}
+        replace_slash=${strip_suffix//\//-}
+        replace_dots=${replace_slash//\./-}
+        echo 'ARTEFACT_NAME='$replace_dots >> $GITHUB_ENV
+
+    - name: 'Upload screenshots artefact'
+      if: failure()
+      uses: actions/upload-artifact@v2
+      with:
+        name: ${{ env.ARTEFACT_NAME }}
+        path: ./scrnsht/
+
+  update-after-e2e-status:
+    if: always()
+    needs: e2e
+    runs-on: ubuntu-latest
+    env:
+      URL_ROOT: https://api.github.com/repos/HumanBrainProject/interactive-viewer
+      SHA: ${{ github.event.inputs.sha }}
+    steps:
+      - name: update badge
+        run: |
+          [[ '${{ needs.e2e.outputs.failure-state }}' = 'true' ]] && state=failure || state=success &&
+          curl -v \
+            -X POST \
+            -H "Authorization: Token ${{ secrets.WORKFLOW_TOKEN }}" \
+            -H 'accept: application/vnd.github.v3+json' \
+            ${URL_ROOT}/statuses/${SHA} \
+            -d '{
+              "target_url":"'$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID'",
+              "description": "e2e completed successfully.",
+              "state": "'$state'"
+            }'
diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml
deleted file mode 100644
index d1314ff604576fab9df447eb3bd006588a08ba4d..0000000000000000000000000000000000000000
--- a/.github/workflows/frontend.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-name: Frontend Tests (Karma + Mocha + Chai)
-
-on: [ push ]
-
-jobs:
-  build:
-    if: "!contains(github.event.head_commit.message, '[skip ci]')"
-    runs-on: ubuntu-latest
-    strategy:
-      matrix:
-        node-version: [10.x, 12.x]
-    steps:
-    - uses: actions/checkout@v1
-    - name: Use Node.js ${{ matrix.node-version }}
-      uses: actions/setup-node@v1
-      with:
-        node-version: ${{ matrix.node-version }}
-    - name: npm install and test
-      run: |
-        npm i
-        npm run lint
-        npm test
-      env:
-        NODE_ENV: test
diff --git a/.openshift/okd_branch_tmpl.yaml b/.openshift/okd_branch_tmpl.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..ca1ae511fe20e595c89d9de066806424b67d771c
--- /dev/null
+++ b/.openshift/okd_branch_tmpl.yaml
@@ -0,0 +1,147 @@
+apiVersion: v1
+kind: Template
+metadata:
+  name: siibra-explorer-branch-deploy
+  annotations:
+    description: "Deploy branch of siibra-explorer"
+    tags: "nodejs,siibra-explorer"
+objects:
+- apiVersion: v1
+  kind: DeploymentConfig
+  metadata:
+    name: siibra-explorer-branch-deploy-${SANITIZED_BRANCH_NAME}
+    labels:
+      app: siibra-explorer-branch-deploy-${SANITIZED_BRANCH_NAME}
+  spec:
+    replicas: 3
+    revisionHistoryLimit: 10
+    selector:
+      deploymentconfig: siibra-explorer-branch-deploy-${SANITIZED_BRANCH_NAME}
+    template:
+      metadata:
+        labels:
+          app: siibra-explorer-branch-deploy
+          deploymentconfig: siibra-explorer-branch-deploy-${SANITIZED_BRANCH_NAME}
+      spec:
+          containers:
+          - env:
+            - name: SESSION_SECRET
+              value: ${SESSION_SECRET}
+            - name: HOSTNAME
+              value: https://siibra-explorer.apps-dev.hbp.eu
+            - name: HOST_PATHNAME
+              value: /${SANITIZED_BRANCH_NAME}
+            - name: IAV_STAGE
+              value: ${SANITIZED_BRANCH_NAME}
+            - name: BUILD_TEXT
+              value: ${BUILD_TEXT}
+            - name: SCRIPT_SRC
+              value: '["stats-dev.humanbrainproject.eu"]'
+            - name: REDIS_PASSWORD
+              valueFrom:
+                secretKeyRef:
+                  key: database-password
+                  name: redis-rate-limiting-db-ephemeral
+            - name: REGIONAL_FEATURE_ENDPOINT_ARRAY
+              value: '["https://jugit.fz-juelich.de/x.gui/20201104_ieegcoord_output/-/raw/master/output/brainscape-json-left-v2.json","https://jugit.fz-juelich.de/x.gui/20201104_ieegcoord_output/-/raw/master/output/brainscape-json-right-v2.json","https://jugit.fz-juelich.de/x.gui/20201113_receptornewui/-/raw/master/output/receptor.json"]'
+            envFrom:
+            - configMapRef:
+                name: hbp-oauth-config-map
+            - configMapRef:
+                name: fluent-logging
+            - configMapRef:
+                name: plugins
+            - configMapRef:
+                name: other-deploy-config
+
+            image: "docker-registry.ebrains.eu/siibra/siibra-explorer:${BRANCH_NAME}"
+            imagePullPolicy: Always
+            livenessProbe:
+              failureThreshold: 3
+              httpGet:
+                path: /${SANITIZED_BRANCH_NAME}/ready
+                port: 8080
+                scheme: HTTP
+              initialDelaySeconds: 10
+              periodSeconds: 10
+              successThreshold: 1
+              timeoutSeconds: 1
+            readinessProbe:
+              failureThreshold: 3
+              httpGet:
+                path: /${SANITIZED_BRANCH_NAME}/ready
+                port: 8080
+                scheme: HTTP
+              initialDelaySeconds: 3
+              periodSeconds: 10
+              successThreshold: 1
+              timeoutSeconds: 6
+            name: siibra-explorer-${SANITIZED_BRANCH_NAME}
+            ports:
+            - containerPort: 8080
+              protocol: TCP
+            resources: {}
+            terminationMessagePath: /dev/termination-log
+            terminationMessagePolicy: File
+          dnsPolicy: ClusterFirst
+          restartPolicy: Always
+          schedulerName: default-scheduler
+          securityContext: {}
+          terminationGracePeriodSeconds: 30
+- apiVersion: v1
+  kind: Service
+  metadata:
+    labels:
+      app: siibra-explorer-branch-deploy-${SANITIZED_BRANCH_NAME}
+    name: siibra-explorer-branch-deploy-${SANITIZED_BRANCH_NAME}
+  spec:
+    ports:
+    - name: 8080-tcp
+      port: 8080
+      protocol: TCP
+      targetPort: 8080
+    selector:
+      deploymentconfig: siibra-explorer-branch-deploy-${SANITIZED_BRANCH_NAME}
+    type: ClusterIP
+- apiVersion: v1
+  kind: Route
+  metadata:
+    labels:
+      app: siibra-explorer-branch-deploy-${SANITIZED_BRANCH_NAME}
+    name: siibra-explorer-branch-deploy-${SANITIZED_BRANCH_NAME}
+  spec:
+    host: siibra-explorer.apps${PATH_POSTFIX}.hbp.eu
+    path: /${SANITIZED_BRANCH_NAME}
+    port:
+      targetPort: 8080-tcp
+    tls:
+      insecureEdgeTerminationPolicy: Redirect
+      termination: edge
+    to:
+      kind: Service
+      name: siibra-explorer-branch-deploy-${SANITIZED_BRANCH_NAME}
+      weight: 100
+    wildcardPolicy: None
+
+parameters:
+- description: Session secret
+  from: '[A-Z0-9]{16}'
+  generate: expression
+  name: SESSION_SECRET
+- name: BRANCH_NAME
+  required: true
+- name: SANITIZED_BRANCH_NAME
+  required: true
+  description: |
+    A lot of routing/naming follow special rules:
+    - does not allow special characters, except for - or . .
+    - only allows lower case.
+    Strip all special characters from BRANCH_NAME, change to all lower case and pass it as SANITIZED_BRANCH_NAME
+- name: PATH_POSTFIX
+  description: url path postfix. either '-dev' or '' (empty string)
+  value: ''
+- name: BUILD_TEXT
+  description: 'UI displaying which build'
+  value: 'dev build'
+labels:
+  template: siibra-explorer-branch-deploy-template
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 3dd82d8838774cd081502c8987b820f0512129d3..0000000000000000000000000000000000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,148 +0,0 @@
-language: node_js
-
-os: linux
-dist: xenial
-node_js:
-- 12
-
-git:
-  depth: 3
-
-# skipping install and script at root
-# as otherwise, travis will run npm install / npm test
-install: skip
-script: skip
-
-jobs:
-  include:
-    - stage: Unit tests, Lint & test build
-      if: |
-        type = push AND \
-        branch NOT IN (master, staging, dev)
-      name: Unit test frontend / Lint
-      install:
-      - npm i
-      script:
-      - npm run lint
-      - npm test
-      - npm run build-aot
-      env:
-      - NODE_ENV=test
-
-    - name: Unit test backend
-      if: |
-        type = push AND \
-        branch NOT IN (master, staging, dev)
-      before_install:
-      - cd deploy
-      install:
-      - npm i
-      script:
-      - npm test
-      env:
-      - NODE_ENV=test
-      - PORT=12234
-
-    - stage: Check version numbers and release notes
-      name: Check version number advanced (package.json)
-      if: |
-        (type = pull_request AND branch = staging) OR \
-        (type = pull_request AND branch = master AND NOT head_branch = staging)
-      script:
-      - MASTER_VERSION=$(git show origin/master:package.json | jq '.version')
-      - THIS_VERSION=$(jq '.version' < package.json)
-      - test "$MASTER_VERSION" == "$THIS_VERSION" && exit 1 || exit 0
-
-    - name: Check release notes authored (docs/releases/v?.?.?.md)
-      if: |
-        (type = pull_request AND branch = staging) OR \
-        (type = pull_request AND branch = master AND NOT head_branch = staging)
-      script:
-      - |
-        VERSION_NUM=$(jq '.version' < package.json) && \
-        VERSION_NUM=${VERSION_NUM#\"} && \
-        VERSION_NUM=${VERSION_NUM%\"} && \
-        test -f docs/releases/v$VERSION_NUM.md && exit 0 || exit 1
-    - name: Release notes linked in mkdocs.yml
-      if: |
-        (type = pull_request AND branch = staging) OR \
-        (type = pull_request AND branch = master AND NOT head_branch = staging)
-      script:
-      - VERSION_NUM=$(jq '.version' < package.json)
-      - VERSION_NUM=${VERSION_NUM#\"} 
-      - VERSION_NUM=${VERSION_NUM%\"} 
-      - 'echo "VERSION_NUM: $VERSION_NUM"'
-      - cat mkdocs.yml
-      - GREP_VERSION_NUM=$(cat mkdocs.yml | grep $VERSION_NUM)
-      - 'echo GREP_VERSION_NUM: $GREP_VERSION_NUM'
-      - test -z "$GREP_VERSION_NUM" && exit 1 || exit 0
-
-    - stage: Create github release
-      if: |
-        type = push AND \
-        branch = master
-      before_script:
-      - chmod ugo+x ./release.sh
-      script: ./release.sh
-      env:
-      - secure: "mERkfztriSCECF/liheC4zFmq4h+MxausY8N0soTytOWUuTmz+NfxObsQ8w8Zl0h3XVe0BZdueJ15zeoipNBDisHZ2kbfLYnCJ8nsym+0O7mpYUSEiT5nDVE0Yj/9a+ZOAW0h0mvWNOwQZId0uTPr8rEst4IvCr+HkLZEUOM06lnftz1edYN3EsVih6MuTymXNLUaYkMBCErq2AFGvgOy6oP6Mq/D2QkBvM+RUNIxcJ2SrM9lMw4i5L/PeOnxbm0MBUndFhR/P0rJ7qz0j4kRP3DvgXfeW+hQsYcECGsnxMgC+Q59ULTBrRGuD03PnHI+7H8CrX+8maHYy+yf7S/iblaCcr0E5gZq/U0mUJbicymBhw4Ygu76X2Rj6E4jUeoC60MqDZovSisV6xuRXXFDqbfYxlBgFSsSS4Ozbl/RUA5MtLx/N4LwAKv5kafktFwZih/ZfQMAOZQfUDB60y4Mvh1Eg6XK4HhnRxdBE8GWP1tQLq/3n8NLr9KBIR4ceHUNEFXjWw3nPIAujnCqUoxSJotfDEnFUpV6KMgZxiC9vv/vq7IgnzzTXRGdvFfjzBbFmYv4+nqYzZhqKb7BI9y++0v+4Jdu9eHGdYVIpiYzzEKhqImK6I4CsUdcakXiPecdxlwA1zc/yET8qyBNGPGnXvYAw1hBt1VbtP8Rf3PKfo="
-
-
-    # Temporarily disabling browserstack e2e tests. They seem to fail without any reason
-
-    # - stage: browserstack e2e
-    #   # only triggered via API, where env can be overwritten
-    #   if: NOT env(LOCAL_TEST_E2E) = 1
-    #   name: e2e (with browserstack)
-    #   install:
-    #   - npm i
-    #   script:
-    #   - PROTRACTOR_SPECS=./src/navigating/*.e2e-spec.js BROWSERSTACK_TEST_NAME=e2e_navigating npm run e2e
-    #   env:
-    #   - ATLAS_URL=https://interactive-viewer-next.apps-dev.hbp.eu/
-    #   - BROWSERSTACK_USERNAME=xiao33
-    #   - secure: "YD2hDBnWzcMs9mTJCsKkJimd+mYKP8V1lTaCnxNvspJUxTuBWFmr8cvryIs9G9DhwgxkC3YL7hugsGkwMg6OIq27vLlo8mgoKS7/qrkWAJApGvDW4jc4CHpI2iE/ryrwG1bI3u9TuG0kSw+2sN/786LBgArlf5NbmwB9zmW4zyzjXXzSME34cwYdfEP96L2cob/uGiIj9YdaA1k3zfBhQQlp328i/xzYbIAcwfIia1AKYh/wgCzj+yfWDQ0Rtg9VcI2JiNfcbzMCgvDEBzshgeXuubFd9GPqPsc8zJhYqAi/15ge+WiB8R50MnZsYHO39JJihQzKz6WxIZQDeOQ2xd600NhFFLg6WPdE3jxAyENouTAd+0zJgXEeUU71YBDBl6RViagf8k7eOe9oMPW5ZlevdD3vcI8BC/qUL6Evye8QDDNi0s8gbIvcnJl5QMRBpeYcm/QaRUow1YeJobpccj/3tb7qTbc7T4Rha/NRBNhbhp/WzDSO/BUSEtpgJ3YwSEPTiEeSocTRT8ylnhEtBB70h4vQSClV73lW4vn7WjdZUTRACdxFNJ1MteQJ+3bgzyWMhDtdQo6BSz2UxF0mQFayAu2p9j0+MbB7x2zW9tksSw+6B6EjzPhQw6eOs2K0+syxWg09MTW1Fy6n0Zgchn0RWSnEPqPvss6kB2pkAR4="
-
-env:
-  global:
-    - CHROMIUM_VERSION=80.0.3987.106
-    - PPTR_VERSION=2.1.0
-    - PROTRACTOR_SPECS=./src/navigating/*.e2e-spec.js
-
-
-# addons:
-#   browserstack:
-#     username: "xiao33"
-#     access_key:
-#       secure: "j0NdVLyNwm1gDclEeE/xYrXAYiYAlx3HQxNRHMFhJyFml5R22spEMTwrTRl/vzyhv1FwfJAKfh4qbOn99cZ5Dzm7fWc8+Kq1zpp/1PRTzbFaLluJkV1wCwoODZkzmSVPj43M6070FhCJvOfe5VRUV440CgZH8IWRm7xaxRnN/MVyFMErMV/GIczEBB7D7E4mMhe6c9pBxjmojDDP4rGvKLGOYU7oVQKgZtbHtP/BxjQ7uzMysdTHZGZ/2c/XW/2bKVSADi4vFzge5PMVF3nSH+vzA09ro180Q5aoaek4XQPoIza0s0cqtqkbvkbJ+lWRE+Q7wJDhQLM4WNx5GX3fegJiqJRT7272EgGAUy6C+e2F+D5nPucf3w6Uov9vBn5zZjbfXdNah3GZEXOTRNAVzstySiwiZe7/f4bk0vWIiEhHC+iutjn8skMxFnuw2eM3SJ5ayjxskHOdRux+1fuDya32ctx8y9a3XLhuFcuGTaeMSAn5Dw5qOlI5Qoc+xRSARoRWKmlEuxTUudD0e+b8xqfZgmOP7D3GZ6QX2W4yFrOLGqUzEySHr8hxxzhIlfwSvVdJ15AtN2AtPFQYQXb7M+XX1L7fr39Z/5ctr7DDgljSE3F2U5ofyWV2hh54aGMBQe76cZfVzF4bi98X3r6u0b3Knyti2pvx5jIoxP46nOA="
-
-
-# to run e2e tests, send API request with the following payload
-# {
-#  "request": {
-#  "message": "API request: e2e on deployed staging build",
-#  "branch":"chore_debugTravisE2e",
-#  "merge_mode": "deep_merge",
-#  "config": {
-#    "env": {
-#      "global": [
-#         "PROTRACTOR_SPECS=./src/navigating/*.e2e-spec.js",
-#         "CHROMIUM_VERSION=80.0.3987.106",
-#         "PPTR_VERSION=2.1.0"
-#      ],
-#      "jobs": [
-#        "ATLAS_URL=https://interactive-viewer-staging.apps.hbp.eu/",
-#        "ATLAS_URL=https://atlases.ebrains.eu/viewer-staging/"
-#      ]
-#    },
-#    "install": [
-#      "npm i",
-#      "npm run wd -- update --versions.chrome=${CHROMIUM_VERSION}",
-#      "npm i --no-save puppeteer@${PPTR_VERSION}"
-#    ],
-#    "script": [
-#      "echo Running e2e on $ATLAS_URL with protractor specs $PROTRACTOR_SPECS",
-#      "npm run e2e"
-#     ]
-#   }
-# }}
diff --git a/Dockerfile b/Dockerfile
index f6bad9ab6d0db5a0534eb1baafe774b8080f3627..d469e99f1a22cd150caa654d421486765c06e02a 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -6,17 +6,29 @@ ENV BACKEND_URL=${BACKEND_URL}
 ARG DATASET_PREVIEW_URL
 ENV DATASET_PREVIEW_URL=${DATASET_PREVIEW_URL:-https://hbp-kg-dataset-previewer.apps.hbp.eu/v2}
 
+ARG BS_REST_URL
+ENV BS_REST_URL=${BS_REST_URL:-https://siibra-api-latest.apps-dev.hbp.eu/v1_0}
+
 ARG STRICT_LOCAL
 ENV STRICT_LOCAL=${STRICT_LOCAL:-false}
 
 ARG KIOSK_MODE
 ENV KIOSK_MODE=${KIOSK_MODE:-false}
 
+ARG MATOMO_URL
+ENV MATOMO_URL=${MATOMO_URL}
+
+ARG MATOMO_ID
+ENV MATOMO_ID=${MATOMO_ID}
+
 COPY . /iv
 WORKDIR /iv
 
+# When building in local, where node_module already exist, prebuilt binary may throw an error
+RUN rm -rf ./node_modules
+
 ARG VERSION
-ENV VERSION=${VERSION:-devNext}
+ENV VERSION=${VERSION}
 
 RUN npm i
 RUN npm run build-aot
@@ -31,17 +43,8 @@ WORKDIR /iv
 
 RUN for f in $(find . -type f); do gzip < $f > $f.gz && brotli < $f > $f.br; done
 
-# Building doc
-FROM python:3.7 as doc-builder
-
-COPY . /iav
-WORKDIR /iav
-
-RUN pip install mkdocs mkdocs-material mdx_truly_sane_lists errandkun
-RUN mkdocs build
-
 # prod container
-FROM node:12-alpine 
+FROM node:12-alpine
 
 ENV NODE_ENV=production
 
@@ -58,12 +61,15 @@ COPY --from=builder /iv/deploy .
 # Copy built interactive viewer
 COPY --from=compressor /iv ./public
 
-# Copy docs
-COPY --from=doc-builder /iav/site ./docs
-
 # Copy the resources files needed to respond to queries
 # is this even necessary any more?
 COPY --from=compressor /iv/res/json ./res
+
+RUN chown -R node:node /iv-app
+
+USER node
 RUN npm i
 
-ENTRYPOINT [ "node", "server.js" ]
\ No newline at end of file
+EXPOSE 8080
+ENV PORT 8080
+ENTRYPOINT [ "node", "server.js" ]
diff --git a/README.md b/README.md
index 966c6405a53284d2c1e6a6a12828578977802bbe..c6e738a0957b6dc71ebe988167863aff00f053a6 100644
--- a/README.md
+++ b/README.md
@@ -7,135 +7,32 @@ Interactive Atlas Viewer is an frontend module wrapping around [nehuba](https://
 A live version of the Interactive Atlas Viewer is available at [https://interactive-viewer.apps.hbp.eu](https://interactive-viewer.apps.hbp.eu). This section is useful for developers who would like to develop this project.
 
 ### General information
+
 Interactive atlas viewer is built with [Angular (v9.0)](https://angular.io/), [Bootstrap (v4)](http://getbootstrap.com/), and [fontawesome icons](https://fontawesome.com/). Some other notable packages used are [ngrx/store](https://github.com/ngrx/platform) for state management. 
 
 Releases newer than [v0.2.9](https://github.com/HumanBrainProject/interactive-viewer/tree/v0.2.9) also uses a nodejs backend, which uses [passportjs](http://www.passportjs.org/) for user authentication, [express](https://expressjs.com/) as a http framework.
 
-### Develop viewer
+### Develop
 
 #### Prerequisites
 
 - node >= 12
 
-#### Buildtime environments
+#### Environments
 
 It is recommended to manage your environments with `.env` file.
 
-As interactive atlas viewer uses [webpack define plugin](https://webpack.js.org/plugins/define-plugin/), where necessary, the environmental variables are `JSON.stringify`'ed and directly replaced in the code.
-
-| name | description | default | example |
-| --- | --- | --- | --- |
-| `VERSION` | printed in console on viewer startup | `GIT_HASH` \|\| unspecificed hash | v2.2.2 |
-| `PRODUCTION` | if the build is for production, toggles optimisations such as minification | `undefined` | true |
-| `BACKEND_URL` | backend that the viewer calls to fetch available template spaces, parcellations, plugins, datasets | `null` | https://interactive-viewer.apps.hbp.eu/ |
-| `DATASET_PREVIEW_URL` | dataset preview url used by component <https://github.com/fzj-inm1-bda/kg-dataset-previewer>. Useful for diagnosing issues with dataset previews.| https://hbp-kg-dataset-previewer.apps.hbp.eu/datasetPreview | http://localhost:1234/datasetPreview |
-| `MATOMO_URL` | base url for matomo analytics | `null` | https://example.com/matomo/ |
-| `MATOMO_ID` | application id for matomo analytics | `null` | 6 |
-| `STRICT_LOCAL` | hides **Explore** and **Download** buttons. Useful for offline demo's | `false` | `true` |
-| `KIOSK_MODE` | after 5 minutes of inactivity, shows overlay inviting users to interact | `false` | `true` |
-| `BUILD_TEXT` | overlay text at bottom right of the viewer. set to `''` to hide. | |
+##### Buildtime environments
 
-#### Deploy environments
+Please see [build_env.md](build_env.md)
 
-It is recommended to manage your environments with `.env` file.
+##### Deploy environments
 
-##### Application
-
-| name | description | default | example |
-| --- | --- | --- | --- |
-| `PORT` | port to listen on | 3000 |
-| `HOST_PATHNAME` | pathname to listen on, restrictions: leading slash, no trailing slash | `''` | `/viewer` |
-| `SESSIONSECRET` | session secret for cookie session |
-| `NODE_ENV` | determines where the built viewer will be served from | | `production` |
-| `PRECOMPUTED_SERVER` | redirect data uri to another server. Useful for offline demos | | `http://localhost:8080/precomputed/` |
-| `LOCAL_CDN` | rewrite cdns to local server. useful for offlnie demo | | `http://localhost:7080/` |
-| `PLUGIN_URLS` | semi colon separated urls to be returned when user queries plugins | `''`
-| `STAGING_PLUGIN_URLS` | semi colon separated urls to be returned when user queries plugins | `''`
-| `USE_LOGO` | possible values are `hbp`, `ebrains`, `fzj` | `hbp` | `ebrains` |
-| `__DEBUG__` | debug flag | 
-
-##### ebrains user authentication
-
-| name | description | default | example |
-| --- | --- | --- | --- |
-| `HOSTNAME` | 
-| `HBP_CLIENTID` | `{HOSTNAME}{HOST_PATHNAME}/hbp-oidc/cb` |
-| `HBP_CLIENTSECRET` |
-| `HBP_CLIENTID_V2` | `{HOSTNAME}{HOST_PATHNAME}/hbp-oidc-v2/cb`
-| `HBP_CLIENTSECRET_V2` | 
-
-##### Querying ebrains knowledge graph
-
-| name | description | default | example |
-| --- | --- | --- | --- |
-| `REFRESH_TOKEN` |
-| `ACCESS_TOKEN` | **nb** as access tokens are usually short lived, this should only be set for development purposes 
-| `CACHE_DATASET_FILENAME` | | `deploy/dataset/cachedKgDataset.json` |
-| `KG_ROOT` | | `https://kg.humanbrainproject.eu/query` |
-| `KG_SEARCH_VOCAB` | | `https://schema.hbp.eu/myQuery/` |
-| `KG_DATASET_SEARCH_QUERY_NAME` | | `interactiveViewerKgQuery-v0_3` |
-| `KG_DATASET_SEARCH_PATH` | | `/minds/core/dataset/v1.0.0` |
-| `KG_SEARCH_SIZE` | | `1000` |
-| `KG_SPATIAL_DATASET_SEARCH_QUERY_NAME` | | `iav-spatial-query-v2` |
-| `KG_SPATIAL_DATASET_SEARCH_PATH` | | `/neuroglancer/seeg/coordinate/v1.0.0` | 
-
-##### Logging
-
-| name | description | default | example |
-| --- | --- | --- | --- |
-| `FLUENT_PROTOCOL` | protocol for fluent logging | `http` |
-| `FLUENT_HOST` | host for fluent logging | `localhost` |
-| `FLUENT_PORT` | port for fluent logging | 24224 |
-| `IAV_NAME` | application name to be logged | `IAV` | 
-| `IAV_STAGE` | deploy of the application | `unnamed-stage` |
-
-##### CSP
-
-| name | description | default | example |
-| --- | --- | --- | --- |
-| `DISABLE_CSP` | disable csp | | `true` |
-| `CSP_REPORT_URI` | report uri for csp violations | `/report-violation` |
-| `NODE_ENV` | set to `production` to disable [`reportOnly`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only) | `null` |
-| `SCRIPT_SRC` | `JSON.stringify`'ed array of allowed scriptSrc | `[]` |
-| `CSP_CONNECT_SRC` | `JSON.stringify`'ed array of allowed dataSrc | `[]` |
-| `WHITE_LIST_SRC` | `JSON.stringify`'ed array of allowed src | `[]` |
-| `PROXY_HOSTNAME_WHITELIST` |
-
-##### Rate limiting
-
-| name | description | default | example |
-| --- | --- | --- | --- |
-| `REDIS_PROTO` | fall back to `REDIS_RATE_LIMITING_DB_EPHEMERAL_PORT_6379_TCP_PROTO` |
-| `REDIS_ADDR` | fall back to `REDIS_RATE_LIMITING_DB_EPHEMERAL_PORT_6379_TCP_ADDR` |
-| `REDIS_PORT` | fall back to `REDIS_RATE_LIMITING_DB_EPHEMERAL_PORT_6379_TCP_PORT` |
-| `REDIS_USERNAME` |
-| `REDIS_PASSWORD` |
-| `DISABLE_LIMITER` | disable rate limiting (maybe required for automated tests) |
-
-##### SaneUrl
-
-| name | description | default | example |
-| --- | --- | --- | --- |
-| `OBJ_STORAGE_ROOT_URL` |
-| `HBP_V2_REFRESH_TOKEN` |
-| `HBP_V2_ACCESS_TOKEN` |
-
-##### Test deploy denvironments
-
-| name | description | default | example |
-| --- | --- | --- | --- |
-| `SERVICE_ACCOUNT_CRED` | 
-| `SERVICE_ACCOUNT_CRED_PATH` | 
-| `WAXHOLM_RAT_GOOGLE_SHEET_ID` |
-| `SKIP_RETRY_TEST` | retry tests contains some timeouts, which may slow down tests | 
+Please see [deploy_env.md](deploy_env.md)
 
 ##### e2e test environments
 
-| name | description | default | example | 
-| --- | --- | --- | --- |
-| PROTRACTOR_SPECS | specs relative to `./e2e/` | `./src/**/*.prod.e2e-spec.js` |  |
-| DISABLE_CHROME_HEADLESS | disable headless chrome, spawns chrome window | `unset` (falsy) | 1 |
-| ENABLE_GPU | uses GPU. nb, in headless mode, will show requirement not met | `unset` (falsy) | 1 |
+Please see [e2e_env.md](e2e_env.md)
 
 #### Start dev server
 
diff --git a/build_env.md b/build_env.md
new file mode 100644
index 0000000000000000000000000000000000000000..0cb122e3194e0efbf775678d65a0590bdd4f4063
--- /dev/null
+++ b/build_env.md
@@ -0,0 +1,16 @@
+# Build-time environment variables
+
+As interactive atlas viewer uses [webpack define plugin](https://webpack.js.org/plugins/define-plugin/), where necessary, the environmental variables are `JSON.stringify`'ed and directly replaced in the code.
+
+| name | description | default | example |
+| --- | --- | --- | --- |
+| `VERSION` | printed in console on viewer startup | `GIT_HASH` \|\| unspecificed hash | v2.2.2 |
+| `PRODUCTION` | if the build is for production, toggles optimisations such as minification | `undefined` | true |
+| `BACKEND_URL` | backend that the viewer calls to fetch available template spaces, parcellations, plugins, datasets | `null` | https://interactive-viewer.apps.hbp.eu/ |
+| `BS_REST_URL` | [siibra-api](https://github.com/FZJ-INM1-BDA/siibra-api) used to fetch different resources | https://siibra-api-latest.apps-dev.hbp.eu/v1_0 |
+| `DATASET_PREVIEW_URL` | dataset preview url used by component <https://github.com/fzj-inm1-bda/kg-dataset-previewer>. Useful for diagnosing issues with dataset previews.| https://hbp-kg-dataset-previewer.apps.hbp.eu/datasetPreview | http://localhost:1234/datasetPreview |
+| `MATOMO_URL` | base url for matomo analytics | `null` | https://example.com/matomo/ |
+| `MATOMO_ID` | application id for matomo analytics | `null` | 6 |
+| `STRICT_LOCAL` | hides **Explore** and **Download** buttons. Useful for offline demo's | `false` | `true` |
+| `KIOSK_MODE` | after 5 minutes of inactivity, shows overlay inviting users to interact | `false` | `true` |
+| `BUILD_TEXT` | overlay text at bottom right of the viewer. set to `''` to hide. | |
diff --git a/common/constants.js b/common/constants.js
index 5734896dff5a3729ddd0a0c244af30df0ce8a0c4..2ba7fc2bb8c3a9ca8dcdf85ba95d770e2dbb9d2b 100644
--- a/common/constants.js
+++ b/common/constants.js
@@ -6,6 +6,8 @@
     OPEN: 'Open',
     EXPAND: 'Expand',
     COLLAPSE: 'Collapse',
+    COPY_TO_CLIPBOARD: 'Copy to clipboard',
+    OPEN_IN_NEW_WINDOW: 'Open in a new window',
 
     // dataset specific
     EXPLORE_DATASET_IN_KG: `Explore dataset in Knowledge Graph`,
@@ -31,7 +33,7 @@
     ZOOM_OUT: 'Zoom out',
     MAXIMISE_VIEW: 'Maximise this view',
     UNMAXIMISE_VIEW: 'Undo maximise',
-    STATUS_PANEL: 'Viewre status panel',
+    STATUS_PANEL: 'Viewer status panel',
     SHOW_FULL_STATUS_PANEL: 'Show full status panel',
     HIDE_FULL_STATUS_PANEL: 'Hide full status panel',
     TOGGLE_SIDE_PANEL: 'Toggle side panel',
@@ -44,6 +46,8 @@
     SHARE_CUSTOM_URL_DIALOG: 'Dialog for creating a custom URL',
 
     // parcellation region specific
+    PARC_VER_SELECT: 'Select parcellation versions',
+    PARC_VER_CONTAINER: 'List of parcellation versions',
     GO_TO_REGION_CENTROID: 'Navigate to region centroid',
     SHOW_ORIGIN_DATASET: `Show probabilistic map`,
     SHOW_CONNECTIVITY_DATA: `Show connectivity data`,
@@ -52,7 +56,20 @@
 
     // additional volumes
     TOGGLE_SHOW_LAYER_CONTROL: `Show layer control`,
-    ADDITIONAL_VOLUME_CONTROL: 'Additional volumes control'
+    ADDITIONAL_VOLUME_CONTROL: 'Additional volumes control',
+
+    //Viewer mode
+    VIEWER_MODE_ANNOTATING: 'annotating',
+
+    // Annotations
+    USER_ANNOTATION_LIST: 'user annotations footer',
+    USER_ANNOTATION_IMPORT: 'Import annotations',
+    USER_ANNOTATION_EXPORT: 'Export all of my annotations',
+    USER_ANNOTATION_EXPORT_SINGLE: 'Export annotation',
+    USER_ANNOTATION_HIDE: 'user annotations hide',
+    USER_ANNOTATION_DELETE: 'Delete annotation',
+    GOTO_ANNOTATION_ROI: 'Navigate to annotation location of interest',
+    EXIT_ANNOTATION_MODE: 'Exit annotation mode'
   }
 
   exports.IDS = {
@@ -61,6 +78,8 @@
   }
 
   exports.CONST = {
+    CANNOT_DECIPHER_HEMISPHERE: 'Cannot decipher region hemisphere.',
+    DOES_NOT_SUPPORT_MULTI_REGION_SELECTION: `Please only select a single region.`,
     MULTI_REGION_SELECTION: `Multi region selection`,
     REGIONAL_FEATURES: 'Regional features',
     NO_ADDIONTAL_INFO_AVAIL: `Currently, no additional information is linked to this region.`,
@@ -76,5 +95,24 @@
     RECEPTOR_FP_CAPTION: `The receptor densities are visualized as fingerprints (fp), which provide the mean density and standard deviation for each of the analyzed receptor types, averaged across samples.`,
     RECEPTOR_PR_CAPTION: `For a single tissue sample, an exemplary density distribution for a single receptor from the pial surface to the border between layer VI and the white matter.`,
     RECEPTOR_AR_CAPTION: `An exemplary density distribution of a single receptor for one laminar cross-section in a single tissue sample.`,
+
+    DATA_NOT_READY: `Still fetching data. Please try again in a few moments.`,
+    QUICKTOUR_HEADER: `Welcome to ebrains siibra explorer`,
+    PERMISSION_TO_QUICKTOUR: `Would you like a quick tour?`,
+    QUICKTOUR_OK: `Start`,
+    QUICKTOUR_NEXTTIME: `Not now`,
+    QUICKTOUR_CANCEL: `Dismiss`,
+  }
+
+  exports.QUICKTOUR_DESC ={
+    REGION_SEARCH: `Use the region quick search for finding, selecting and navigating brain regions in the selected parcellation map.`,
+    ATLAS_SELECTOR: `This is the atlas selector. Click here to choose between EBRAINS reference atlases of different species.`,
+    CHIPS: `These "chips" indicate the currently selected parcellation map as well as selected region. Click the chip to see different versions, if any. Click (i) to read more about a selected item. Click (x) to clear a selection.`,
+    SLICE_VIEW: `The planar views allow you to zoom in to full resolution (mouse wheel), pan the view (click+drag), and select oblique sections (shift+click+drag). You can double-click brain regions to select them.`,
+    PERSPECTIVE_VIEW: `The 3D view gives an overview of the brain with limited resolution. It can be independently rotated. On the 3d view you can find additional settings.`,
+    VIEW_ICONS: `Use these icons in any of the views to maximize it and zoom in/out.`,
+    TOP_MENU: `These icons provide access to plugins, pinned datasets, and user documentation. Use the profile icon to login with your EBRAINS account.`,
+    LAYER_SELECTOR: `This is the atlas layer browser. If an atlas supports multiple template spaces or parcellation maps, you will find them here.`,
+    STATUS_CARD: `This is the coordinate navigator. Expand it to manipulate voxel and physical coordinates, to reset the view, or to create persistent links to the current view for sharing.`,
   }
 })(typeof exports === 'undefined' ? module.exports : exports)
diff --git a/common/helpOnePager.md b/common/helpOnePager.md
index f23b9a498e529d2cfc65b5856c0b313142f29448..a8551e0b2b22258b633459a5854774ac301f8ae0 100644
--- a/common/helpOnePager.md
+++ b/common/helpOnePager.md
@@ -1,26 +1,17 @@
 # Quickstart
 
-## Desktop
 
-| Action | Key |
+| Action | Desktop | Touch devices |
 | --- | --- |
-| Translate / Pan | `[drag]` any of the slice views |
-| Oblique rotation | `<shift>` + `[drag]` any of the slice views |
-| Zoom | `[mousewheel]` |
-| Zoom | `[hover]` on any slice views, `[click]` magnifier |
-| Next slice | `<ctrl>` + `[mousewheel]` |
-| Next 10 slice | `<ctrl>` + `<shift>` + `[mousewheel]` |
-| Toggle delineation | `[q]` |
+| Translate / Pan | `[drag]` any of the slice views | `[drag]` any of the slice views |
+| Oblique rotation | `<shift>` + `[drag]` any of the slice views | `[rotate]` any of the slice views |
+| Zoom | `[mousewheel]` | `[pinch zoom]` |
+| Zoom | `[hover]` on any slice views > `[click]` magnifier | `[tap]` on magnifier |
+| Next slice | `<ctrl>` + `[mousewheel]` | - |
+| Next 10 slice | `<ctrl>` + `<shift>` + `[mousewheel]` | - |
+| Toggle delineation | `[q]` | - |
 
 ---
 
-## Touch devices
 
-> You can enable touch interface by `[Portrait]` > `Settings` > `Enable Mobile UI`
-
-| Action | Key |
-| --- | --- |
-| Translate / Pan | `[drag]` any of the slice views |
-| Oblique rotation | `[rotate]` any of the slice views |
-| Zoom | `[pinch zoom]` |
-| Zoom | `[tap]` on magnifier |
+You can enable touch interface by `[Portrait]` > `Settings` > `Enable Mobile UI`
\ No newline at end of file
diff --git a/common/util.js b/common/util.js
index cc2ec69652524585d9cbece49d2ae9823184b1e1..5432e7c276ebda4871bae1ff1c63779914f69631 100644
--- a/common/util.js
+++ b/common/util.js
@@ -25,15 +25,15 @@
   }
 
   const HEMISPHERE = {
-    LEFT_HEMISPHERE: `left hemisphere`,
-    RIGHT_HEMISPHERE: `right hemisphere`
+    LEFT_HEMISPHERE: `left`,
+    RIGHT_HEMISPHERE: `right`
   }
 
   exports.getRegionHemisphere = region => {
     if (!region) return null
-    return (region.name && region.name.includes('- right hemisphere') || (!!region.status && region.status.includes('right hemisphere')))
+    return (region.name && region.name.includes(' right') || (!!region.status && region.status.includes('right')))
       ? HEMISPHERE.RIGHT_HEMISPHERE
-      : (region.name && region.name.includes('- left hemisphere') || (!!region.status && region.status.includes('left hemisphere')))
+      : (region.name && region.name.includes(' left') || (!!region.status && region.status.includes('left')))
         ? HEMISPHERE.LEFT_HEMISPHERE
         : null
   }
@@ -51,14 +51,13 @@
    *
    * https://stackoverflow.com/a/16348977/6059235
    */
-  exports.intToRgb = int => {
-    if (int >= 65500) {
-      return [255, 255, 255]
-    }
-    const str = String(int * 65535)
+  exports.strToRgb = str => {
+    if (typeof str !== 'string') throw new Error(`strToRgb input must be typeof string !`)
+
     let hash = 0
-    for (let i = 0; i < str.length; i++) {
-      hash = str.charCodeAt(i) + ((hash << 5) - hash);
+    // run at least 2 cycles, or else, len 1 string does not get hashed well
+    for (let i = 0; i < str.length || i < 5; i++) {
+      hash = str.charCodeAt(i % str.length) + ((hash << 5) - hash);
     }
     const returnV = []
     for (let i = 0; i < 3; i++) {
@@ -77,6 +76,13 @@
 
   exports.getIdObj = getIdObj
 
+  exports.getIdFromKgIdObj = kg => {
+    if(kg.kgId && kg.kgSchema) {
+      return `${kg.kgSchema}/${kg.kgId}`
+    }
+    return null
+  }
+
   exports.getIdFromFullId = fullId => {
     const idObj = getIdObj(fullId)
     if (!idObj) return null
@@ -128,7 +134,7 @@
         await (() => new Promise(rs => setTimeout(rs, timeout)))()
       }
     }
-  
+
     throw new Error(`fn failed ${retries} times. Aborting.`)
   }
   const flattenRegions = regions => regions.concat(
@@ -180,4 +186,58 @@
       )
     )
   }
+
+  exports.serialiseParcellationRegion = ({ ngId, labelIndex }) => {
+    if (!ngId) {
+      throw new Error(`#serialiseParcellationRegion error: ngId must be defined`)
+    }
+
+    if (!labelIndex) {
+      throw new Error(`#serialiseParcellationRegion error labelIndex must be defined`)
+    }
+
+    return `${ngId}#${labelIndex}`
+  }
+
+  const deserialiseParcRegionId = labelIndexId => {
+    const _ = labelIndexId && labelIndexId.split && labelIndexId.split('#') || []
+    const ngId = _.length > 1
+      ? _[0]
+      : null
+    const labelIndex = _.length > 1
+      ? Number(_[1])
+      : _.length === 0
+        ? null
+        : Number(_[0])
+    return { labelIndex, ngId }
+  }
+
+  exports.deserialiseParcRegionId = deserialiseParcRegionId
+
+  exports.deserialiseParcellationRegion = ({ region, labelIndexId, inheritedNgId = 'root' }) => {
+    const { labelIndex, ngId } = deserialiseParcRegionId(labelIndexId)
+  }
+
+  const getPad = ({ length, pad }) => {
+    if (pad.length !== 1) throw new Error(`pad needs to be precisely 1 character`)
+    return input => {
+      const padNum = Math.max(input.toString().length - length, 0)
+      const padString = Array(padNum).fill(pad).join('')
+      return `${padString}${input}`
+    }
+  }
+
+  exports.getDateString = () => {
+    const d = new Date()
+    const pad2 = getPad({ pad: '0', length: 2 })
+
+    const year = d.getFullYear()
+    const month = d.getMonth() + 1
+    const date = d.getDate()
+
+    const hr = d.getHours()
+    const min = d.getMinutes()
+    return `${year}${pad2(month)}${pad2(date)}_${pad2(hr)}${pad2(min)}`
+  }
+
 })(typeof exports === 'undefined' ? module.exports : exports)
diff --git a/common/util.spec.js b/common/util.spec.js
index 4597a54db648521dd029fb002f22ef56436d3b51..a66f12a39bd1307fbc17573f078937e37d742350 100644
--- a/common/util.spec.js
+++ b/common/util.spec.js
@@ -1,4 +1,4 @@
-import { getIdFromFullId } from './util'
+import { getIdFromFullId, strToRgb } from './util'
 
 describe('common/util.js', () => {
   describe('getIdFromFullId', () => {
@@ -17,4 +17,63 @@ describe('common/util.js', () => {
       expect(getIdFromFullId(fullId)).toBe(`minds/core/parcellationregion/v1.0.0/a844d80f-1d94-41a0-901a-14ae257519db`)
     })
   })
+
+  describe('strToRgb', () => {
+    const str1 = 'hello world'
+    const str2 = 'foo bar'
+    const str3 = 'a'
+    const str4 = 'b'
+    const strArr = [
+      str1,
+      str2,
+      str3,
+      str4,
+    ]
+    it('should return rgb', () => {
+      const outs = strArr.map(strToRgb)
+      for (const out of outs) {
+        expect(
+          out instanceof Array
+        ).toBeTruthy()
+
+        expect(out.length).toEqual(3)
+
+        for (const n of out) {
+          expect(n).toBeGreaterThanOrEqual(0)
+          expect(n).toBeLessThanOrEqual(255)
+        }
+      }
+      
+    })
+
+    it('rgb returned should be disinct', () => {
+
+      const outs = strArr.map(strToRgb)
+      for (let i = 0; i < outs.length; i++) {
+        const compareA = outs[i]
+        for (let j = i + 1; j < outs.length; j++) {
+          const compareB = outs[j]
+          // compare all generated rgb, expect at least 1 of rgb to be of greater than 5 units out
+          expect(
+            compareA.some((n, idx) => Math.abs( n - compareB[idx] ) > 5)
+          ).toBeTruthy()
+        }
+      }
+    })
+
+    it ('should throw if not providing stirng', () => {
+      expect(() => {
+        strToRgb(12)
+      }).toThrow()
+      
+      expect(() => {
+        strToRgb(['hello world'])
+      }).toThrow()
+
+      expect(() => {
+        strToRgb({foo: 'baz'})
+      }).toThrow()
+    })
+  
+  })
 })
diff --git a/deploy/app.js b/deploy/app.js
index e382142fafe4a21acf581f9af58430f76c68f7c8..9192aa73ba7eeee36bdd5198dbf0eb0a2834f4a8 100644
--- a/deploy/app.js
+++ b/deploy/app.js
@@ -3,13 +3,12 @@ const path = require('path')
 const express = require('express')
 const app = express.Router()
 const session = require('express-session')
-const MemoryStore = require('memorystore')(session)
 const crypto = require('crypto')
 const cookieParser = require('cookie-parser')
+const bkwdMdl = require('./bkwdCompat')()
 
 const { router: regionalFeaturesRouter, regionalFeatureIsReady } = require('./regionalFeatures')
-const { router: datasetRouter, ready: datasetRouteIsReady } = require('./datasets')
-const { router: saneUrlRouter, ready: saneUrlIsReady } = require('./saneUrl')
+const { router: datasetRouter } = require('./datasets')
 
 const LOCAL_CDN_FLAG = !!process.env.PRECOMPUTED_SERVER
 
@@ -42,53 +41,75 @@ app.use((req, _, next) => {
 })
 
 /**
- * load env first, then load other modules
+ * configure Auth
+ * async function, but can start server without
  */
 
-const { configureAuth, ready: authReady } = require('./auth')
+let authReady
+
+const _ = (async () => {
 
 /**
- * memorystore (or perhaps lru-cache itself) does not properly close when server shuts
- * this causes problems during tests
- * So when testing app.js, set USE_DEFAULT_MEMORY_STORE to true
- * see app.spec.js
+ * load env first, then load other modules
  */
-const { USE_DEFAULT_MEMORY_STORE } = process.env
-const store = USE_DEFAULT_MEMORY_STORE
-  ? (console.warn(`USE_DEFAULT_MEMORY_STORE is set to true, memleak expected. Do NOT use in prod.`), null)
-  : new MemoryStore({
+
+  const { configureAuth, ready } = require('./auth')
+  authReady = ready
+  const store = await (async () => {
+
+    const { USE_DEFAULT_MEMORY_STORE } = process.env
+    if (!!USE_DEFAULT_MEMORY_STORE) {
+      console.warn(`USE_DEFAULT_MEMORY_STORE is set to true, memleak expected. Do NOT use in prod.`)
+      return null
+    } 
+
+    const { _initPr, redisURL, StoreType } = require('./lruStore')
+    await _initPr
+    console.log('StoreType', redisURL, StoreType)
+    if (!!redisURL) {
+      const redis = require('redis')
+      const RedisStore = require('connect-redis')(session)
+      const client = redis.createClient({
+        url: redisURL
+      })
+      return new RedisStore({
+        client
+      })
+    }
+    
+    /**
+     * memorystore (or perhaps lru-cache itself) does not properly close when server shuts
+     * this causes problems during tests
+     * So when testing app.js, set USE_DEFAULT_MEMORY_STORE to true
+     * see app.spec.js
+     */
+    const MemoryStore = require('memorystore')(session)
+    return new MemoryStore({
       checkPeriod: 86400000
     })
+    
+  })() 
 
-const SESSIONSECRET = process.env.SESSIONSECRET || 'this is not really a random session secret'
-
-/**
- * passport application of oidc requires session
- */
-app.use(session({
-  secret: SESSIONSECRET,
-  resave: true,
-  saveUninitialized: false,
-  store
-}))
+  const SESSIONSECRET = process.env.SESSIONSECRET || 'this is not really a random session secret'
 
-/**
- * configure CSP
- */
-if (process.env.DISABLE_CSP && process.env.DISABLE_CSP === 'true') {
-  console.warn(`DISABLE_CSP is set to true, csp will not be enabled`)
-} else {
-  require('./csp')(app)
-}
+  /**
+   * passport application of oidc requires session
+   */
+  app.use(session({
+    secret: SESSIONSECRET,
+    resave: true,
+    saveUninitialized: false,
+    store
+  }))
 
-/**
- * configure Auth
- * async function, but can start server without
- */
-
-(async () => {
   await configureAuth(app)
   app.use('/user', require('./user'))
+
+  /**
+   * saneUrl end points
+   */
+  const { router: saneUrlRouter } = require('./saneUrl')
+  app.use('/saneUrl', saneUrlRouter)
 })()
 
 const PUBLIC_PATH = process.env.NODE_ENV === 'production'
@@ -122,7 +143,7 @@ if (LOCAL_CDN_FLAG) {
     indexFile = data.replace(regex, LOCAL_CDN)
   })
   
-  app.get('/', (_req, res) => {
+  app.get('/', bkwdMdl, (_req, res) => {
     if (!indexFile) return res.status(404).end()
     res.setHeader('Content-Type', 'text/html; charset=utf-8')
     return res.status(200).send(indexFile)
@@ -144,7 +165,21 @@ app.use(require('./devBanner'))
  * populate nonce token
  */
 const { indexTemplate } = require('./constants')
-app.get('/', cookieParser(), (req, res) => {
+app.get('/', (req, res, next) => {
+  
+  /**
+   * configure CSP
+   */
+  if (process.env.DISABLE_CSP && process.env.DISABLE_CSP === 'true') {
+    console.warn(`DISABLE_CSP is set to true, csp will not be enabled`)
+    next()
+  } else {
+    const { bootstrapReportViolation, middelware } = require('./csp')
+    bootstrapReportViolation(app)
+    middelware(req, res, next)
+  }
+
+}, bkwdMdl, cookieParser(), (req, res) => {
   const iavError = req.cookies && req.cookies['iav-error']
   
   res.setHeader('Content-Type', 'text/html')
@@ -167,15 +202,12 @@ app.get('/', cookieParser(), (req, res) => {
 app.use('/logo', require('./logo'))
 
 app.get('/ready', async (req, res) => {
-  const authIsReady = await authReady()
+  const authIsReady = authReady ? await authReady() : false
   const regionalFeatureReady = await regionalFeatureIsReady()
-  const datasetReady = await datasetRouteIsReady()
-  const saneUrlReady = await saneUrlIsReady()
+
   const allReady = [ 
     authIsReady,
     regionalFeatureReady,
-    datasetReady,
-    saneUrlReady,
     /**
      * add other ready endpoints here
      * call sig is await fn(): boolean
@@ -194,11 +226,6 @@ if (LOCAL_CDN_FLAG) setAlwaysOff(true)
 
 app.use(compressionMiddleware, express.static(PUBLIC_PATH))
 
-/**
- * saneUrl end points
- */
-app.use('/saneUrl', saneUrlRouter)
-
 const jsonMiddleware = (req, res, next) => {
   if (!res.get('Content-Type')) res.set('Content-Type', 'application/json')
   next()
diff --git a/deploy/app.spec.js b/deploy/app.spec.js
index e5b9499f3937e5d9b128632f9ed4086b6a9d4081..5427e5d80d0b450b2dba959375fc46c6c4cefe47 100644
--- a/deploy/app.spec.js
+++ b/deploy/app.spec.js
@@ -1,6 +1,4 @@
-const { expect } = require('chai')
-const fs = require('fs')
-const { assert } = require('console')
+const { expect, assert } = require('chai')
 const express = require('express')
 const got = require('got')
 const sinon = require('sinon')
@@ -17,9 +15,9 @@ describe('authentication', () => {
   process.env['USE_DEFAULT_MEMORY_STORE'] = true
 
   const fakeFunctionObj = {
-    fakeAuthConfigureAuth: (req, res, next) => next(),
+    fakeAuthConfigureAuth: sinon.stub().callsFake((req, res, next) => next()),
     fakeAuthReady: async () => true,
-    fakeUserRouterFn: (req, res, next) => res.status(200).send()
+    fakeUserRouterFn: sinon.stub().callsFake((req, res, next) => res.status(200).send())
   }
 
   before(async () => {
@@ -63,19 +61,32 @@ describe('authentication', () => {
   })
 
   after(() => {
-    delete require.cache[require.resolve('./saneUrl')]
     delete require.cache[require.resolve('./datasets')]
+    delete require.cache[require.resolve('./saneUrl')]
     delete require.cache[require.resolve('./user')]
     delete require.cache[require.resolve('./constants')]
     server.close()
   })
+  it('> auth middleware is called', async () => {
+    await got(`http://localhost:${PORT}/user`)
+    assert(
+      fakeFunctionObj.fakeAuthConfigureAuth.called,
+      'auth middleware should be called'
+    )
+  })
+
+  it('> user middleware called', async () => {
+    await got(`http://localhost:${PORT}/user`)
+    assert(
+      fakeFunctionObj.fakeUserRouterFn.called,
+      'user middleware is called'
+    )
+  })
   
   it('fakeAuthConfigureAuth is called before user router', async () => {
-    const spyFakeAuthConfigureAuth = sinon.spy(fakeFunctionObj, 'fakeAuthConfigureAuth')
-    const spyFakeUserRouterFn = sinon.spy(fakeFunctionObj, 'fakeUserRouterFn')
     await got(`http://localhost:${PORT}/user`)
     assert(
-      spyFakeAuthConfigureAuth.calledBefore(spyFakeUserRouterFn),
+      fakeFunctionObj.fakeAuthConfigureAuth.calledBefore(fakeFunctionObj.fakeUserRouterFn),
       'fakeAuthConfigureAuth is called before user router'
     )
   })
diff --git a/deploy/atlas/index.js b/deploy/atlas/index.js
index da353ffa3e830fdf885911693b8520ddfe22d89e..9bf745f2e071849efba3128ebb53f329bf44f27f 100644
--- a/deploy/atlas/index.js
+++ b/deploy/atlas/index.js
@@ -56,6 +56,8 @@ const previewImageFIleNameMap = new Map([
   ['minds/core/parcellationatlas/v1.0.0/2449a7f0-6dd0-4b5a-8f1e-aec0db03679d', 'waxholm-v2.png'],
   ['minds/core/parcellationatlas/v1.0.0/11017b35-7056-4593-baad-3934d211daba', 'waxholm-v1.png'],
   ['juelich/iav/atlas/v1.0.0/79cbeaa4ee96d5d3dfe2876e9f74b3dc3d3ffb84304fb9b965b1776563a1069c', 'short-bundle-hcp.png'],
+  ['julich/tmp/referencespace/freesurfer', 'freesurfer.png'],
+  ['julich/tmp/parcellation/freesurfer-test-parc', 'freesurfer.png'],
 ])
 
 router.get('/preview', (req, res) => {
diff --git a/deploy/atlas/sanity.spec.js b/deploy/atlas/sanity.spec.js
index dbf13733a0f11d936d1d896ee70452d6c52c4835..009a0b41e9ed7b1526ab8ef58525bca66b0eb823 100644
--- a/deploy/atlas/sanity.spec.js
+++ b/deploy/atlas/sanity.spec.js
@@ -9,13 +9,14 @@ const templateFiles = [
   'colin.json',
   'MNI152.json',
   'waxholmRatV2_0.json',
-  'allenMouse.json'
+  'allenMouse.json',
+  'freesurfer.json',
 ]
 
 const atlasFiles = [
   'atlas_multiLevelHuman.json',
   'atlas_waxholmRat.json',
-  'atlas_allenMouse.json'
+  'atlas_allenMouse.json',
 ]
 
 const templateIdToParcsMap = new Map()
diff --git a/deploy/bkwdCompat/index.js b/deploy/bkwdCompat/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..bd1c6d9fc418346c6df7703cb29c924f8091d8f7
--- /dev/null
+++ b/deploy/bkwdCompat/index.js
@@ -0,0 +1,24 @@
+module.exports = () =>{
+  const urlState = require('./urlState')
+  return (req, res, next) => {
+    const query = req.query || {}
+
+    let errorMessage = ``
+    const redir = urlState(query, err => {
+      errorMessage += err
+    })
+    if (errorMessage !== '') {
+      res.cookie(
+        `iav-error`,
+        errorMessage,
+        {
+          httpOnly: true,
+          sameSite: 'strict',
+          maxAge: 1e3 * 30
+        }
+      )
+    }
+    if (redir) return res.redirect(redir)
+    next()
+  }
+}
diff --git a/deploy/bkwdCompat/ngLinks.json b/deploy/bkwdCompat/ngLinks.json
new file mode 100644
index 0000000000000000000000000000000000000000..68909f4cd82b39a4e80fab9d6964f29dba3dee18
--- /dev/null
+++ b/deploy/bkwdCompat/ngLinks.json
@@ -0,0 +1,705 @@
+[
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22D%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..11SkX%7E.7gJ8%7E.jul_%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%2254%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1MqM.26Pd8%7E.4qaI%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22Z%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..VsYi%7E.4PMQx%7E.3AKQY..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%222t%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..Eodw%7E.o4bm.k5Ii%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__49095056.24218857_-8144216.081099838_30427371.198444664__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_PoC-PrC_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%223d%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..F8VI%7E.oD3y.jwZC%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221m%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..JEVh%7E.1SwCV.fBI6%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%221k%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..ZUOO%7E.ni50.pm0m%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%224b%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..3K4.1w6ta%7E.HD1..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%223J%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..ERGS%7E.nxOi.kGEi%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%221m%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..DdeY%7E.nbr0.kciC%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%224I%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2Yr.1w6Je%7E.CqH..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%221z%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1QVh.26PTm%7E.4dte%7E..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%225I%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..ZPje%7E.k-EW.aB2C%7E..2Ul9",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%223k%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1US2.26O_W%7E.4Mco%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__42371717.17171717_2476767.676767677_-39825252.52525252__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_IT-MT_2.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%224d%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2xm.1w6aq%7E.E-t..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22COLIN_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221l%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0.._Is8~.3aur6~.2HVsr~..ndB",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221p%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..3g1go%7E.1UQMR%7E.1WpNX..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22k%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..28BoF%7E.1Cmwa%7E.2xRKp..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__35704140.66067894_14382478.307200491_5407444.055411786__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_Op-Ins_0.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1TQy~.1lJnu~.2_Ap..16de",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%2217%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..3D5Fm%7E.1gtcf.qaP_..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%223i%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2Ahku.2zQH0%7E.2fvZq..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%223%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1MGE.26Pd8%7E.4sKo%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%221B%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2HV-C.2qgk4%7E.2alb4..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%2230%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..YggE%7E.jwia.qLYK%7E..2Ul9",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%225I%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..ZAsO%7E.nn8S.q7Pe%7E..2Ul9",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%223y%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1QVh.26PTm%7E.4dte%7E..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%225J%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..ZPW_%7E.nk3a.ps5q%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%224b%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1P-z.26PWu%7E.4fkO%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%2218%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1Nwh.26Pd8%7E.4n0K%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22i%22%7D",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%22X%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2Amey.2zJqW%7E.2fy1e..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%224H%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..29D.1w60u%7E.AfP..3q90",
+  "parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&templateSelected=MNI+Colin+27&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-8600000_-60400000_-26300000__200000&regionsSelected=251&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FJuBrain%2Fv2.2c%2FPMaps%2FCerebellum_Ninterp.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%2215%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..3Cyo2%7E.qSUh.u8iu..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%221n%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..BBwc%7E.nkwG.hPrG%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Long%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__19455161.915305838_-35445751.45308608_-9003182.950456694__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FDWM_atlas%2Fright-hemisphere%2FProbability_Maps%2FCingulum_Temporal_Right.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221k%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..X5dD%7E.2Vnjq.1Uykm%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__45152357.49472204_-43779380.71780436_12545742.434904993__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_MT-SM_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%2230%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..E2CO%7E.nnMW.kRQK%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221R%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..1V12k%7E.1Rafk%7E.i8ka%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%2214%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2rVY9%7E.2V0N5%7E.2fk1M..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%223j%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2AX2W.2zcYy%7E.2fqkS..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%224f%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1P-z.26PWu%7E.4fkO%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%224h%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1Tf8.26P6K%7E.4QYM%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__22396467.333475202_30278782.719727606_33071823.79229623__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_RMF-SF_1.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__35781514.5451747_24855086.96321377_2943897.1833153665__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_Tr-Ins_0.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%224a%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2xm.1w6aq%7E.E-t..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%228%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..6mG.1w9sC%7E.gS4..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%221i%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..ZUOO%7E.ni50.pm0m%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221S%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..3LcF0%7E.35JHr%7E.2S1oD..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%22V%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..29NWG.2qeA0%7E.2mtZa..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%22z%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..Bh0m%7E.nyLe.hCkC%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%223G%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1TEZ.26PB0%7E.4SUu%7E..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%221r%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..AN0M%7E.1xeqK%7E.AlJC..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%221y%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..0.1w4W0%7E.0..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%224g%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1Tf8.26P6K%7E.4QYM%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Long%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__20204555.010511562_-18738822.70497547_12101681.850035042__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FDWM_atlas%2Fright-hemisphere%2FProbability_Maps%2FCorticoSpinalTract_Right.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22v%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..335oh%7E.1NytF%7E.17UrZ..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%2256%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1k_.1w5k8%7E.8V7..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%224e%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..3K4.1w6ta%7E.HD1..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22U%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..XCQ3~.3OOwB~.1uR2-~..ndB",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%224Z%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..AOuM%7E.1xbHm%7E.Asek..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%221j%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..YaQE%7E.j9T0.sS28%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%22U%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..31quy%7E.O-Sl%7E.6FB-..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%22R%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..29GIW.2qnDi%7E.2mzMG..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%221A%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2Ao_y.2zGW0%7E.2fzIu..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%2219%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2HTuq.2qk10%7E.2ajvG..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%223h%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2HKgC.2qwM4%7E.2adEi..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%222V%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..ZOpM%7E.l0Hi.aD_U%7E..2Ul9",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%221%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..F4hO%7E.Fq70.a7rq%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%222x%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2HFZe.2r028%7E.2aZzK..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221Q%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2iOlI%7E.I45Y%7E.po6i..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%221t%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..7EQ.1wAOa%7E.lFP..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%2219%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2HTuq.2qk10%7E.2ajvG..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%2222%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..533.1w8DW%7E.SRV..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B\"jubrain+mni152+v18+left\"%3A\"U\"%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..335JG~.RmCa~.9xvN..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%222%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..F4hO%7E.Fq70.a7rq%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%2214%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..E2CO%7E.nnMW.kRQK%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B\"jubrain+colin+v18+right\"%3A\"U\"%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..3L8Du.ACUF~.CuB..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%225H%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..ZAsO%7E.nn8S.q7Pe%7E..2Ul9",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__42957534.246575326_24265753.424657524_24589954.337899536__1071975.4977029096&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.eu%2Fprecomputed%2FJuBrain%2F17%2Ficbm152casym%2Fpmaps%2FIFS_ifs2_r_N10_nlin2MNI152ASYM2009C_2.2_publicDOI_022d278402aab813fcb610330f53c4e7.nii.gz",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Long%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-10742512.128243_-5019457.920269981_25404450.537861213__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FDWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2FCingulum_Long_Left.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%2220%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2PHl0.2r5J8%7E.2SHoC..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%2214%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..E2CO%7E.nnMW.kRQK%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%221y%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..0.1w4W0%7E.0..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__35508261.35936913_-18041119.03867817_14042621.104018018__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_SM-Ins_0.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%224b%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..3K4.1w6ta%7E.HD1..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-43877177.177177176_19509609.609609604_24809609.609609604__1071975.4977029096&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.eu%2Fprecomputed%2FJuBrain%2F17%2Ficbm152casym%2Fpmaps%2FIFS_ifs4_l_N10_nlin2MNI152ASYM2009C_2.2_publicDOI_57c4832619f167ab18995996c02d8295.nii.gz",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%221t%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..9yuf%7E.26OmS%7E.3Ngp..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221T%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2f_6l%7E.Dh6k.2QAOP..45jd",
+  "templateSelected=Big+Brain+%28Histology%29&parcellationSelected=Cytoarchitectonic+Maps&navigation=0_0_0_1__0.7809706330299377_0.11322952806949615_-0.15641532838344574_-0.5939680933952332__1922235.5293810747__3187941_-50436480_3430986__350000&regionsSelected=v1%231",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221V%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..1FHD2%7E.1Q12N%7E.3v05K..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%224H%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1OT1.26Pba%7E.4lCs%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%222u%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..FIyd%7E.oH9J.jrG8%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%223K%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..Bqe4%7E.o0HC.h8RC%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%222w%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2HN6S.2qtNS%7E.2aeuy..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%224X%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1RwH.26PKO%7E.4YF0%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__8990859.4242488_-48760138.68459761_30669993.696154654__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_IC-PrCu_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%223a%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2HPSS.2qqLi%7E.2agZC..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%221h%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1U30.26P3C%7E.4ObC%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%221-%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1Q_Z.26PQe%7E.4c0G%7E..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%221j%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..ZZIz%7E.nfVO.pffm%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%228%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1UqM.26Ovq%7E.4Kdm%7E..3q90",
+  "templateSelected=Big+Brain+%28Histology%29&parcellationSelected=Cytoarchitectonic+Maps&regionsSelected=interpolated%2317&navigation=0_0_0_1__0.3140767216682434_-0.7418519854545593_0.4988985061645508_-0.3195493221282959__1922235.5293810747__-3723402.984494186_-33064127.136934616_32569711.99852508__295893.7198067633",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__30645299.14529915_-26158119.658119664_39324786.324786335__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_PrC-SP_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-36661740.006718166_9149311.387302637_28015787.705744028__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_CMF-Op_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%222u%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..FIyd%7E.oH9J.jrG8%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__51195404.63603091_-14323302.155347705_2863155.754371688__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_ST-TT_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=DiFuMo+Atlas+%28128+dimensions%29",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221Y%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..Wb5l%7E.2NMIB%7E.3OeH2..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22c%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..pAuP~.3SYJE~.1vwl0~..ndB",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%221x%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..Sd.1w4qK%7E.26b..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%2227%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..5gX.1w8nS%7E.X2B..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%221%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..F4hO%7E.Fq70.a7rq%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%2219%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2Amey.2zJqW%7E.2fy1e..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_RIGHT_NG_SPLIT_HEMISPHERE%22%3A%221l%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..r2hg.3YhlF~.2C-PV~..ndB",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%221A%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2HTuq.2qk10%7E.2ajvG..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%221j%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..YaQE%7E.j9T0.sS28%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%2220%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2HWqK.2wfeK%7E.2Ws-i..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%223d%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..CFOu%7E.oAn4.gxpO%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%2254%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1MqM.26Pd8%7E.4qaI%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-28928541.42602122_15938569.778431177_31573243.72571668__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_Op-SF_0.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%221z%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..3hh.1w78m%7E.JQm..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%225H%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..ZAsO%7E.nn8S.q7Pe%7E..2Ul9",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%221-%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..4Ol.1w7h8%7E.Nu_..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%221y%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1LhX.26Pba%7E.4u4W%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-10029072.082835525_-53669653.524492234_28416367.980884105__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_IC-PrCu_0.nii",
+  "templateSelected=Big+Brain+%28Histology%29&parcellationSelected=Cytoarchitectonic+Maps&regionsSelected=interpolated%2316&navigation=0_0_0_1__0.3140767216682434_-0.7418519854545593_0.4988985061645508_-0.3195493221282959__1922235.5293810747__-13546343.062011965_-38230308.814068206_26252295.549827665__295893.7198067633",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%225I%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..acOg%7E.jAXm.bhQy%7E..2Ul9",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%222%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..F4hO%7E.Fq70.a7rq%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221G%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..3Gd1S%7E.149OK%7E.LUnD..2-zd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%221z%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..3hh.1w78m%7E.JQm..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Long%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-23101959.038290292_-23612644.701691896_-317453.25022260845__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FDWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2FFornix_Left.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%221r%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..9zzE%7E.26MH4%7E.3RIH..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__34410498.05201644_26975097.399178684_-5295303.7801411__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_Or-Ins_0.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%221p%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..ZNsu%7E.l1JK.aGyo%7E..2Ul9",
+  "templateSelected=MNI+Colin+27&parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-39920863.30935252_26585131.89448443_23833812.949640274__1071975.4977029096&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.eu%2Fprecomputed%2FJuBrain%2F17%2Fcolin27%2Fpmaps%2FIFS_ifs1_l_N10_nlin2Stdcolin27_2.2_publicDOI_2fbed54a956057637fdba19857b48e9f.nii.gz",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%2228%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1RwH.26PKO%7E.4YF0%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%221o%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..aaCg%7E.jCXu.bnus%7E..2Ul9",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22l%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..KSQz%7E.11xk-.nMpo..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__11734325.873867199_-77620214.53671166_15244220.454965785__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_Cu-Li_0.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%221t%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..7EQ.1wAOa%7E.lFP..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%224G%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2HTuq.2qk10%7E.2ajvG..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221f%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..1znYa%7E.1O6-R%7E.2dbjG..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-37078878.59349014_24973271.560940832_1607151.3423615992__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_Tr-Ins_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-38807749.35113089_5194846.125324428_-19637931.034482762__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_ST-Ins_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%225K%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..acOg%7E.jAXm.bhQy%7E..2Ul9",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-55690767.97385621_-50960375.81699346_-12476715.686274514__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_IT-MT_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221j%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..XCQ3%7E.3OOwB%7E.1uR2-%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__30586217.364905894_14720400.728597432_36013661.20218578__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_CMF-RMF_0.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%2224%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..5gX.1w8nS%7E.X2B..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22q%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..OW6m%7E.3brPV.BdkD%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%22F%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2AZqe.2zZaK%7E.2frx0..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%225K%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..ZOpM%7E.l0Hi.aD_U%7E..2Ul9",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22r%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..1RaEp%7E.S06b%7E.21uJB%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Long%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-22082652.546646506_-21001765.00252144_14490317.700453863__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FDWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2FCorticoSpinalTract_Left.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%2230%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..BMdc%7E.nple.hLS0%7E..45jd",
+  "templateSelected=MNI%20Colin%2027&parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2n9GK~.2oRG~.dIrs~..2kDL&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.eu%2Fprecomputed%2FJuBrain%2F17%2Fcolin27%2Fpmaps%2FArea-TI2_l_N10_nlin2Stdcolin27_5.1_publicDOI_45fbfa8808ec4de620328650b134bdc3.nii.gz",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%224W%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1RSj.26PNW%7E.4a86%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221L%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..1DZJJ%7E.3a7Lv%7E.3kH3-..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%2218%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1Nwh.26Pd8%7E.4n0K%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%221x%22%7D",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-45744868.5232442_7059968.933762342_21897869.743703544__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_Op-PrC_0.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%221%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..D4LA%7E.GT4Q.aPm4%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-6385010.26694046_-10154346.338124573_35074264.202600956__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_CAC-PrCu_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-12409806.728704363_-46228704.36649965_32741231.20973514__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_PoCi-PrCu_0.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%22A%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..7EQ.1wAOa%7E.lFP..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%2221%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2HWqK.2wfeK%7E.2Ws-i..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Long%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-12056009.31723991_-7192894.092022866_35409882.92948544__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FDWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2FCingulum_Short_Left.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%22X%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2Amey.2zJqW%7E.2fy1e..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%22d%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..F8VI%7E.oD3y.jwZC%7E..45jd",
+  "templateSelected=Big+Brain+%28Histology%29&parcellationSelected=Grey%2FWhite+matter&cNavigation=0.0.0.-W000.._eCwg.2-FUe3._-s_W.2_evlu..7LIx..1uaTK.Bq5o~.lKmo~..NBW&previewingDatasetFiles=%5B%7B%22datasetId%22%3A%22minds%2Fcore%2Fdataset%2Fv1.0.0%2Fb08a7dbc-7c75-4ce7-905b-690b2b1e8957%22%2C%22filename%22%3A%22Overlay+of+data+modalities%22%7D%5D",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%221u%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1VBr.26Or8%7E.4Ido%7E..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%223l%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..6Vx.1w9b0%7E.e45..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Long%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__36737729.44624403_-37269211.784667596_-9835616.227055386__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FDWM_atlas%2Fright-hemisphere%2FProbability_Maps%2FInferiorLongitudinal_Right.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_RIGHT_NG_SPLIT_HEMISPHERE%22%3A%22c%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..euQE.3QFtb~.1xcbg~..ndB",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%2226%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..5ND.1w8WG%7E.Ukc..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%228%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1UqM.26Ovq%7E.4Kdm%7E..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22COLIN_V25_RIGHT_NG_SPLIT_HEMISPHERE%22%3A%221P%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..6TGB.3JtxN~.1sq5b~..ndB",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%223k%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1US2.26O_W%7E.4Mco%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%221B%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2HV-C.2qgk4%7E.2alb4..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Long%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__8399457.111834958_-3368566.7752442956_24279858.84907709__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FDWM_atlas%2Fright-hemisphere%2FProbability_Maps%2FCingulum_Long_Right.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221g%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2aFnh%7E.4M-6A%7E.7xbF..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-36588069.812837295_-23393788.035365716_54053335.62980823__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_PoC-PrC_2.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%224h%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..5y-.1w92e%7E.ZNK..3q90",
+  "templateSelected=Big+Brain+%28Histology%29&parcellationSelected=Cytoarchitectonic+Maps&regionsSelected=interpolated%2318&navigation=0_0_0_1__0.3140767216682434_-0.7418519854545593_0.4988985061645508_-0.3195493221282959__1922235.5293810747__-5344587.965411705_-43655930.97799518_24702722.09890703__295893.7198067633",
+  "templateSelected=MNI+Colin+27&parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-44373029.77232924_23361646.234676003_24951838.87915936__1071975.4977029096&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.eu%2Fprecomputed%2FJuBrain%2F17%2Fcolin27%2Fpmaps%2FIFS_ifs2_l_N10_nlin2Stdcolin27_2.2_publicDOI_5ca6ef9bbc75f8785f3ca7701919d6d2.nii.gz",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-39093195.26627219_-65965976.33136095_4747041.420118347__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_IP-IT_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221B%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..35jwK%7E.Ld2w%7E.DK6y..2-zd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-27356803.1704095_-1168295.904887721_52813077.93923381__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_CMF-SF_0.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%221l%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..ZZIz%7E.nfVO.pffm%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%221l%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..ZZIz%7E.nfVO.pffm%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%221x%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..Sd.1w4qK%7E.26b..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%2224%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..5gX.1w8nS%7E.X2B..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-30273969.319271334_38103787.15244487_12995565.675934792__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_Tr-SF_0.nii",
+  "https://interactive-viewer.apps.hbp.eu/?templateSelected=Waxholm+Space+rat+brain+MRI%2FDTI&parcellationSelected=Waxholm+Space+rat+brain+atlas+v3&cNavigation=0.0.0.-W000..2-8Bnd.2_tvb9._yymE._tYzz..1Sjt..Ecv%7E.Lqll%7E.33ix..9fo&cRegionsSelected=%7B%22v3%22%3A%2213.a.b.19.6.c.q.x.1.1L.Y.1K.r.s.y.z._.1G.-.Z.18.v.f.g.1J.1C.k.14.15.2I.7.1E.1F.2C.2D.21.22.2T.10.11.12.1D.1S.A.1i.1j.1k.1m.1n.1o.1p.2N.2O.2P.1V.1W.1X.1Y.1Z.1a.U.V.W.3.1I.e.d.2J.2K.2L.2M.2a.1T.1H.m.h.2E.2F.2H.1U.o.t.2.17.p.w.4.5.1A.1B.u.l.2U.2V.2W.1x.1_.1-.20.23.24.25.26.27.28.29.2A.2B.2Z.X.1z.j.16.1t.1u.1v.1w.2Y%22%7D",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%224c%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1O_e.26Pa0%7E.4jP4%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__12386666.666666672_23929122.807017535_-20468421.05263158__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_LOF-MOF_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__10317942.486836776_48647428.10854596_-3121911.7051437944__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_RAC-SF_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Long%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__9941482.805374086_-4659681.99186492_35097929.24935289__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FDWM_atlas%2Fright-hemisphere%2FProbability_Maps%2FCingulum_Short_Right.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22COLIN_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22_%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..D3uo%7E.GT_m.aV30%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%221z%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1QVh.26PTm%7E.4dte%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-33746170.921198666_6706659.267480582_-22055382.907880127__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_LOF-ST_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__37122810.153736144_22599392.205934912_10842867.357883453__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_Op-Tr_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Long%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-47643968.64734021_-57403521.41086969_12415566.826119527__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FDWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2FArcuate_Posterior_Left.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__4427358.49056603_-13966981.132075474_33288679.245283023__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_CAC-PrCu_0.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%224G%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2Amey.2zJqW%7E.2fy1e..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%2224%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1TEZ.26PB0%7E.4SUu%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%22A%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1VBr.26Or8%7E.4Ido%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22W%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..WAQu%7E.1mQ2-%7E.1uh1d..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%222%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2KTfX%7E.1Pzv3%7E.32bEt..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%224Z%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..AOuM%7E.1xbHm%7E.Asek..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%223e%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..CFOu%7E.oAn4.gxpO%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%2222%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1SN7.26PHG%7E.4WKq%7E..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-41579978.38422048_13575925.425560653_30199000.270197242__1071975.4977029096&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.eu%2Fprecomputed%2FJuBrain%2F17%2Fcolin27%2Fpmaps%2FIFS_ifj1_l_N10_nlin2Stdcolin27_2.2_publicDOI_cb45fad7eaa1423aa4dd807529a3f689.nii.gz",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%221s%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..AN0M%7E.1xeqK%7E.AlJC..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__35988653.00146411_-30185944.363103956_39836749.63396779__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_PoC-SP_1.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%221l%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..DdeY%7E.nbr0.kciC%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%224%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..ud.1w58e%7E.4E8..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__13205807.680101559_-29051570.929863527_44830688.67026341__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_PoCi-PrCu_1.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221U%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..1V9sm%7E.F4Mm%7E.1WU96%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%223J%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..ERGS%7E.nxOi.kGEi%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%221u%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..6-m.1wA7O%7E.irG..3q90",
+  "parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&templateSelected=MNI+Colin+27&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-27800000_-11000000_-10900000__200000&regionsSelected=18&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FJuBrain%2Fv2.2c%2FPMaps%2FAmygdala_AStr.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%226%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..9_-e%7E.26Jni%7E.3UtA..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%224W%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..4kK.1w7yK%7E.Q9b..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%2254%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1KA.1w5RO%7E.6Lw..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__30558414.858925268_-78159010.35020559_13526229.9730611__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_LO-SP_0.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%224V%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..4Ol.1w7h8%7E.Nu_..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%22d%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..F8VI%7E.oD3y.jwZC%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%224V%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..4Ol.1w7h8%7E.Nu_..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%221s%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..9yuf%7E.26OmS%7E.3Ngp..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22e%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2aQPO%7E.41tbP%7E.xbRD%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22O%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..1-FVe%7E.1XEM6%7E.ajpB..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%221i%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..YUwW%7E.jAMq.sZim%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%224e%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..3K4.1w6ta%7E.HD1..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%221_%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1Q_Z.26PQe%7E.4c0G%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__36690610.56952284_2248973.832734734_43190225.75679836__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_CMF-PrC_1.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-23256396.701205328_23880207.231972933_38552865.2992176__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_RMF-SF_1.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22I%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2q9i0%7E.18W1K%7E.W5Ov..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%2257%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2HRjm.2qnDi%7E.2aiDS..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%2223%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..5ND.1w8WG%7E.Ukc..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%223a%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2HPSS.2qqLi%7E.2agZC..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%2230%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..BMdc%7E.nple.hLS0%7E..45jd",
+  "templateSelected=Big+Brain+%28Histology%29&parcellationSelected=Cytoarchitectonic+Maps&regionsSelected=interpolated%235&navigation=0_0_0_1__0.3140767216682434_-0.7418519854545593_0.4988985061645508_-0.3195493221282959__1922235.5293810747__-11860943.840244282_-3841070.8089927398_6062769.611936631__295893.7198067633",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%224G%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2HTuq.2qk10%7E.2ajvG..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%221l%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..DdeY%7E.nbr0.kciC%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-28154889.85214655_51133454.070423365_6508966.213407755__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_RMF-SF_0.nii",
+  "templateSelected=Big+Brain+%28Histology%29&parcellationSelected=Cytoarchitectonic+Maps&regionsSelected=interpolated%232&navigation=0_0_0_1__0.3140767216682434_-0.7418519854545593_0.4988985061645508_-0.3195493221282959__1922235.5293810747__-9255503.675448196_27432071.93513858_43445688.65496198__295893.7198067633",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-14133600_5746400_38749600__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_PoCi-SF_0.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%22l%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..C6ou%7E.o7U8.g-_a%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-38142764.93369323_13186656.495716482_6789050.580917731__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_Op-Ins_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221w%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..23NGr%7E.5OTEV%7E.PIyY..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%22R%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..29GIW.2qnDi%7E.2mzMG..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%2256%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1NNu.26Pd8%7E.4ooi%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22_%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..F4Rm%7E.FqUI.aApq%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%223d%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..CFOu%7E.oAn4.gxpO%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22g%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..1xoL5%7E.32COZ%7E.2v2sZ..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%223E%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..Dr3w%7E.nhsy.kX3G%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%224V%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1RSj.26PNW%7E.4a86%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=DiFuMo+Atlas+%281024+dimensions%29",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%224f%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..3hh.1w78m%7E.JQm..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-54779380.41779167_-44430368.05456871_5921912.746909201__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_MT-ST_0.nii",
+  "templateSelected=MNI%20152%20ICBM%202009c%20Nonlinear%20Asymmetric&parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2jbzh.2Rif.nV4g~..2kDL&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.eu%2Fprecomputed%2FJuBrain%2F17%2Ficbm152casym%2Fpmaps%2FArea-TI2_r_N10_nlin2MNI152ASYM2009C_5.1_publicDOI_6d04657cb03e80a480d2332fd88ce368.nii.gz",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%224c%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1O_e.26Pa0%7E.4jP4%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-6586523.335081279_15699265.862611443_27273466.177241743__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_PoCi-RAC_0.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-43868606.701940045_19458994.708994716_26839506.172839493__1071975.4977029096&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.eu%2Fprecomputed%2FJuBrain%2F17%2Fcolin27%2Fpmaps%2FIFS_ifs4_l_N10_nlin2Stdcolin27_2.2_publicDOI_9c0d663426f3c3fe44dc19c2e4c524f7.nii.gz",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%224d%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1O_e.26Pa0%7E.4jP4%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%22k%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..Ed94%7E.o06G.kAl8%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-29274193.54838711_-114097.96893668175_-22296893.66786141__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_MOF-ST_0.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__41281305.506216675_14571492.007104814_28173845.470692724__1071975.4977029096&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.eu%2Fprecomputed%2FJuBrain%2F17%2Fcolin27%2Fpmaps%2FIFS_ifj1_r_N10_nlin2Stdcolin27_2.2_publicDOI_c09b6c65860aecdd6c7243bf89270e75.nii.gz",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%22k%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..BzuQ%7E.o3_i.h4BK%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22COLIN_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22U%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..Xcr6~.3TrSY~.1y6gL~..ndB",
+  "templateSelected=MNI%20152%20ICBM%202009c%20Nonlinear%20Asymmetric&parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2oPBr~.98Qh~.eb7c~..2kDL&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.eu%2Fprecomputed%2FJuBrain%2F17%2Ficbm152casym%2Fpmaps%2FArea-TI2_l_N10_nlin2MNI152ASYM2009C_5.1_publicDOI_0d90b238155bc15ca0ec39ca312475a7.nii.gz",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%2221%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2PHl0.2r5J8%7E.2SHoC..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-56748493.68318756_-21383576.287657917_27466569.484936833__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_PoC-SM_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%224g%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1Tf8.26P6K%7E.4QYM%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__39397584.5410628_-60459903.38164252_1968599.033816427__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_IP-IT_0.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22COLIN_V25_RIGHT_NG_SPLIT_HEMISPHERE%22%3A%22c%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..b89c.3VDfZ~.1xOx2~..ndB",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-42369622.98621491_20625726.623484462_29724381.33200465__1071975.4977029096&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.eu%2Fprecomputed%2FJuBrain%2F17%2Ficbm152casym%2Fpmaps%2FIFS_ifs3_l_N10_nlin2MNI152ASYM2009C_2.2_publicDOI_163dfd8bdc1ea401267827a07fe3c938.nii.gz",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22H%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..280PJ%7E.1QPbi%7E.1BBlo..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%225J%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..YUwW%7E.jAMq.sZim%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%2254%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1KA.1w5RO%7E.6Lw..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2LfW4%7E.1k5sq%7E.jhaT..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22COLIN_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221X%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..D46o%7E.GTX0.aSQO%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-45311270.683734_-61189509.83453013_15697315.017171413__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_IP-MT_0.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22COLIN_V25_RIGHT_NG_SPLIT_HEMISPHERE%22%3A%221X%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..D46o%7E.GTX0.aSQO%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%223G%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..5y-.1w92e%7E.ZNK..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%223E%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..Dr3w%7E.nhsy.kX3G%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%2228%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..533.1w8DW%7E.SRV..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-35232426.650366746_26462408.31295845_-5745721.271393642__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_Or-Ins_0.nii",
+  "templateSelected=Big+Brain+%28Histology%29&parcellationSelected=Cytoarchitectonic+Maps&cNavigation=0.0.0.-W000.._YYCk.2-DuEy.-5_Ew.2_VOZ4..4a_0..1GeRe%7E.DrL4%7E.RjPd%7E..Lv_",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%2218%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2REFz%7E.1AMpx%7E.HfJx%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%221y%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1LhX.26Pba%7E.4u4W%7E..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-41744720.49689442_20211801.24223602_31307453.41614905__1071975.4977029096&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.eu%2Fprecomputed%2FJuBrain%2F17%2Fcolin27%2Fpmaps%2FIFS_ifs3_l_N10_nlin2Stdcolin27_2.2_publicDOI_a6839437711ba9eb50fd17baf7c6a562.nii.gz",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__26920246.789221868_16882976.580206513_32365927.977839336__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_Op-SF_0.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%22u%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..ZKgq%7E.nlUC.pxu8%7E..2Ul9",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-14041584.806810752_-23498690.242305174_43994433.52979699__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_PoCi-PrCu_1.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%22V%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..29NWG.2qeA0%7E.2mtZa..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%223J%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..Bqe4%7E.o0HC.h8RC%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%2257%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2HRjm.2qnDi%7E.2aiDS..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%2256%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1NNu.26Pd8%7E.4ooi%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__51600571.42857143_-24701142.85714285_-19193714.285714284__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_IT-MT_1.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%229%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..9zzE%7E.26MH4%7E.3RIH..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__27924207.93018402_41440428.76114589_-12625023.714665145__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_LOF-RMF_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre+Bundle+Atlas+-+Long+Bundle",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%225%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..3gjm9%7E.2CrNs%7E.2GrZ-..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%223i%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2Ahku.2zQH0%7E.2fvZq..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_RIGHT_NG_SPLIT_HEMISPHERE%22%3A%221X%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..F4Rm%7E.FqUI.aApq%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%224f%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1P-z.26PWu%7E.4fkO%7E..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%22v%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..ZFpa%7E.nmPa.q1VW%7E..2Ul9",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__53615750.83170816_-19381495.927497998_27881209.131581962__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_PoC-SM_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%2212%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..pAuP%7E.3SYJE%7E.1vwl0%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%223E%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..BMdc%7E.nple.hLS0%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%224b%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1P-z.26PWu%7E.4fkO%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221K%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2UKBH%7E.ojTW%7E.18kYZ..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%224c%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2Yr.1w6Je%7E.CqH..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__27362756.892230585_38169373.433583945_14147719.298245624__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_Tr-SF_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%221k%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..YaQE%7E.j9T0.sS28%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%221p%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..abJm%7E.jBZO.bkf2%7E..2Ul9",
+  "templateSelected=Big+Brain+%28Histology%29&parcellationSelected=Cytoarchitectonic+Maps&regionsSelected=interpolated%237&navigation=0_0_0_1__0.3140767216682434_-0.7418519854545593_0.4988985061645508_-0.3195493221282959__1922235.5293810747__3479937.1036163364_2702958.4989095596_3819372.2349505564__295893.7198067633",
+  "templateSelected=Big+Brain+%28Histology%29&parcellationSelected=Cytoarchitectonic+Maps&regionsSelected=interpolated%231&navigation=0_0_0_1__0.3140767216682434_-0.7418519854545593_0.4988985061645508_-0.3195493221282959__1922235.5293810747__-10496193.61330948_13643679.341296235_42286811.995786324__295893.7198067633   ",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-37961341.0330446_15978825.794032723_38445139.55726659__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_CMF-RMF_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%2224%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1TEZ.26PB0%7E.4SUu%7E..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%223a%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2AkEG.2zN4K%7E.2fwny..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%2255%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1NNu.26Pd8%7E.4ooi%7E..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%224g%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..6Eu.1w9Jq%7E.bip..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%223%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..Sd.1w4qK%7E.26b..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%222V%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..ZOpM%7E.l0Hi.aD_U%7E..2Ul9",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%221-%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..4Ol.1w7h8%7E.Nu_..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-33746524.06417113_-21555080.213903755_11583957.219251335__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_SM-Ins_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-40688555.85831063_26974659.40054497_22290917.347865567__1071975.4977029096&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.eu%2Fprecomputed%2FJuBrain%2F17%2Ficbm152casym%2Fpmaps%2FIFS_ifs1_l_N10_nlin2MNI152ASYM2009C_2.2_publicDOI_77901443edc477e83f2bb6b47a363873.nii.gz",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%221m%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..BBwc%7E.nkwG.hPrG%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-35109855.33453888_33661618.44484627_-12595840.867992766__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_LOF-Or_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%224a%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1PVg.26PYS%7E.4ha2%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%223K%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..Ed94%7E.o06G.kAl8%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22C%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2iiQ0%7E.1ZJOg%7E.3ZbYl..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__29495619.238842756_-25190334.945696816_57033266.40503785__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_PoC-PrC_1.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%223e%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..Ezks%7E.o8wK.j-vO%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-34393372.462704815_-57501467.35143067_38250550.256786495__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_IP-SP_1.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%221k%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..YaQE%7E.j9T0.sS28%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%2220%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2HWqK.2wfeK%7E.2Ws-i..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%2255%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1KA.1w5RO%7E.6Lw..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22N%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2hIab%7E.4QhvG%7E.1scqJ..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%22F%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2AZqe.2zZaK%7E.2frx0..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Long%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__32097179.28902629_-17118817.61978361_-6205660.741885617__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FDWM_atlas%2Fright-hemisphere%2FProbability_Maps%2FInferiorFrontoOccipital_Right.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%224I%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2Yr.1w6Je%7E.CqH..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-44546301.22405535_23441458.22245875_22606971.79350719__1071975.4977029096&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.eu%2Fprecomputed%2FJuBrain%2F17%2Ficbm152casym%2Fpmaps%2FIFS_ifs2_l_N10_nlin2MNI152ASYM2009C_2.2_publicDOI_3b1bdcf898eaa037f9cfed73620493e0.nii.gz",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%221i%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..ZUOO%7E.ni50.pm0m%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%221o%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..ZNsu%7E.l1JK.aGyo%7E..2Ul9",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%227%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1UqM.26Ovq%7E.4Kdm%7E..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%2257%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2Amey.2zJqW%7E.2fy1e..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22X%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..38tgy%7E.2GAnW%7E.1PP8o..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-24634940.15412362_-27893015.24840139_60979258.8949008__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_PoC-PrC_1.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%225%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1MqM.26Pd8%7E.4qaI%7E..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%221j%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..ZZIz%7E.nfVO.pffm%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%225K%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..acOg%7E.jAXm.bhQy%7E..2Ul9",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%2223%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..5ND.1w8WG%7E.Ukc..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__37928011.20448181_-924089.6358543336_11330812.324929968__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_PrC-Ins_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%22y%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..EEyo%7E.nsUe.kLoy%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%221p%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..abJm%7E.jBZO.bkf2%7E..2Ul9",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%2213%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..uQp7%7E.h1QE%7E.1BE7q%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%224f%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..3hh.1w78m%7E.JQm..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-41312500_-48958333.33333333_34979166.66666667__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_IP-SM_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221i%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2bqCu%7E.2qj3o%7E.16Y0P%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221s%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..b1MU%7E.1GCBj.1HelW%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%2221%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2PHl0.2r5J8%7E.2SHoC..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%229%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..ANz8%7E.1xd2G%7E.Ao-c..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre+Bundle+Atlas+-+Short+Bundle",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221l%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..12Tsg%7E.5hzZ%7E.1HNX0%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221a%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..1w2X6%7E.2sRK1%7E.uijU%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%22V%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..24C3m.2-gDW%7E.2qdoa..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221O%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..IqNM%7E.nseY.mlhR%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-46412228.05701426_-33472564.569713846_41822902.154109955__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_PoC-SM_1.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%224W%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1RSj.26PNW%7E.4a86%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%223d%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..F8VI%7E.oD3y.jwZC%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%224%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2n3_-%7E.4lUyM%7E.1Xzb%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%225K%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..ZOpM%7E.l0Hi.aD_U%7E..2Ul9",
+  "templateSelected=Big+Brain+%28Histology%29&parcellationSelected=Cytoarchitectonic+Maps&regionsSelected=interpolated%2325&navigation=0_0_0_1__0.3140767216682434_-0.7418519854545593_0.4988985061645508_-0.3195493221282959__1922235.5293810747__8584702.92136917_6170347.792104806_-11790982.216547377__295893.7198067633",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%221n%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..Dr3w%7E.nhsy.kX3G%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221v%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..28C12%7E.2nvp1%7E.3oIdM..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__7623812.754409775_-4527815.468113974_32416214.382632285__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_CAC-PoCi_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Long%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-41680974.997759655_-24691818.26328525_32234115.960211486__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FDWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2FArcuate_Anterior_Left.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22M%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..1gs7J%7E.1f0yF%7E.ijVE%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__27903280.22492972_-64708153.70196813_39108997.18837863__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_IP-SP_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%2228%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1RwH.26PKO%7E.4YF0%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%221A%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2HTuq.2qk10%7E.2ajvG..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%221h%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1U30.26P3C%7E.4ObC%7E..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%221A%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2Ao_y.2zGW0%7E.2fzIu..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%22M%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2PL0q.2r45W%7E.2SKKa..45jd",
+  "https://interactive-viewer.apps.hbp.eu/?templateSelected=Waxholm+Space+rat+brain+MRI%2FDTI&parcellationSelected=Waxholm+Space+rat+brain+atlas+v1&cNavigation=0.0.0.-W000..2-8Bnd.2_tvb9._yymE._tYzz..1Sjt..Ecv%7E.Lqll%7E.33ix..9fo&cRegionsSelected=%7B%22v1_01%22%3A%2213.a.b.19.6.c.q.x.1.1L.Y.1K.r.s.y.z._.1G.-.Z.18.v.f.g.1J.1C.k.14.15.7.1E.1F.2.10.11.12.1D.1M.1N.1O.1P.1Q.1R.1S.A.E.F.H.U.V.W.3.1I.e.d.1T.1H.m.h.n.1U.o.t.2.17.p.w.4.5.1A.1B.u.l.X.j.16.i%22%7D",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-41752255.71126896_13437607.986177772_29191111.53772317__1071975.4977029096&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.eu%2Fprecomputed%2FJuBrain%2F17%2Ficbm152casym%2Fpmaps%2FIFS_ifj1_l_N10_nlin2MNI152ASYM2009C_2.2_publicDOI_51cf544971b79d4b9a10fe5b9da00576.nii.gz",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%22X%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2HTuq.2qk10%7E.2ajvG..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%221u%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..6-m.1wA7O%7E.irG..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-47487080.81363387_-14342908.191313908_-799752.6113249063__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_ST-TT_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%222x%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2HFZe.2r028%7E.2aZzK..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%221h%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..6Eu.1w9Jq%7E.bip..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__47412703.19084889_-15053080.473610282_25047662.05097331__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_PrC-SM_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22E%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..1cEIQ%7E.2DcET.1xt_G..45jd",
+  "templateSelected=Big+Brain+%28Histology%29&parcellationSelected=Cytoarchitectonic+Maps&regionsSelected=interpolated%2315&navigation=0_0_0_1__0.3140767216682434_-0.7418519854545593_0.4988985061645508_-0.3195493221282959__1922235.5293810747__-5671180.552151227_-44793672.67154157_21692004.396585546__295893.7198067633",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221E%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2Ysl2%7E.1tZHm%7E.37bdV..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22V%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..3qQpu%7E.kmhV%7E.3PFE..2-zd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-42829457.36434109_-17871834.625322998_43969509.04392764__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_PoC-PrC_3.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%221g%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1U30.26P3C%7E.4ObC%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%2226%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1SpG.26PE8%7E.4UQK%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221l%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0.._PbS~.3T7dn~.2Dwe7~..ndB",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%224d%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2xm.1w6aq%7E.E-t..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%221_%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..42a.1w7Py%7E.LfS..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%221i%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..YUwW%7E.jAMq.sZim%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%227%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..6-m.1wA7O%7E.irG..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__39031433.0339711_19421124.560718477_32263959.39086294__1071975.4977029096&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.eu%2Fprecomputed%2FJuBrain%2F17%2Fcolin27%2Fpmaps%2FIFS_ifs3_r_N10_nlin2Stdcolin27_2.2_publicDOI_634ba65855c32e73a6d9848f6512f62a.nii.gz",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%221q%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..ZPW_%7E.nk3a.ps5q%7E..2Ul9",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%2255%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1KA.1w5RO%7E.6Lw..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22o%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2EA40%7E.19jJ7%7E.NY9L..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%224F%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2Amey.2zJqW%7E.2fy1e..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%224Z%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..9_-e%7E.26Jni%7E.3UtA..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__41907881.163606375_23001341.035692185_27392098.205075294__1071975.4977029096&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.eu%2Fprecomputed%2FJuBrain%2F17%2Ficbm152casym%2Fpmaps%2FIFS_ifs4_r_N10_nlin2MNI152ASYM2009C_2.2_publicDOI_0d57128ee2cd1878ec1c0b36a390ea82.nii.gz",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22b%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..1nY_1%7E.44E3r%7E.lckg%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%225G%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..0.1w4W0%7E.0..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%222u%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..CNhW%7E.oDtW.gtfm%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-43345287.59528759_8643970.893970907_29156964.65696466__1071975.4977029096&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.eu%2Fprecomputed%2FJuBrain%2F17%2Ficbm152casym%2Fpmaps%2FIFS_ifj2_l_N10_nlin2MNI152ASYM2009C_2.2_publicDOI_0c3fa7a162ff6d09b5d7127d68750969.nii.gz",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%223%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..Sd.1w4qK%7E.26b..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%226%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..ANz8%7E.1xd2G%7E.Ao-c..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%222%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..D46o%7E.GTX0.aSQO%7E..45jd",
+  "https://interactive-viewer.apps.hbp.eu/?templateSelected=Waxholm+Space+rat+brain+MRI%2FDTI&parcellationSelected=Waxholm+Space+rat+brain+atlas+v2&cNavigation=0.0.0.-W000..2-8Bnd.2_tvb9._yymE._tYzz..1Sjt..Ecv%7E.Lqll%7E.33ix..9fo&cRegionsSelected=%7B%22undefined%22%3A%2213.a.b.19.6.c.q.x.1.1L.Y.1K.r.s.y.z._.1G.-.Z.18.v.f.g.1J.1C.k.14.15.7.1E.1F.10.11.12.1D.1S.A.1V.1W.1X.1Y.1Z.1a.1i.1j.1k.1m.1n.1o.1p.U.V.W.3.1I.e.d.1T.1H.m.h.n.1U.o.t.2.17.p.w.4.5.1A.1B.u.l.j.16%22%7D",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%22l%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..C6ou%7E.o7U8.g-_a%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%221h%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..6Eu.1w9Jq%7E.bip..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%221_%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1Q_Z.26PQe%7E.4c0G%7E..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__42442095.58823532_19733455.88235292_27535539.21568626__1071975.4977029096&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.eu%2Fprecomputed%2FJuBrain%2F17%2Fcolin27%2Fpmaps%2FIFS_ifs4_r_N10_nlin2Stdcolin27_2.2_publicDOI_9dc7b73fc32e0ace1895b041827fa134.nii.gz",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-14299421.661409035_49790220.82018927_8868296.529968455__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_RAC-SF_1.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%221%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..D4LA%7E.GT4Q.aPm4%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-39311320.75471698_-78719569.21021873_10634079.145099342__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_IP-LO_1.nii",
+  "templateSelected=Big+Brain+%28Histology%29&parcellationSelected=Cytoarchitectonic+Maps&regionsSelected=interpolated%233&navigation=0_0_0_1__0.3140767216682434_-0.7418519854545593_0.4988985061645508_-0.3195493221282959__1922235.5293810747__-8973603.851892563_28973428.94347216_35691249.925134525__295893.7198067633",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__4658634.538152605_19629852.744310558_25204819.27710843__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_PoCi-RAC_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__24132739.93808049_11329979.36016512_49375773.993808046__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_CMF-SF_1.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%221g%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..6Vx.1w9b0%7E.e45..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-31881756.75675676_-17624577.702702716_44101773.64864865__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_CMF-PoC_0.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%222u%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..CNhW%7E.oDtW.gtfm%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-31499165.41478885_34825154.39826405_-13381488.900016688__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_LOF-RMF_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%22l%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..Ezks%7E.o8wK.j-vO%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%224V%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1RSj.26PNW%7E.4a86%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%223l%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1US2.26O_W%7E.4Mco%7E..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%222t%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..BzuQ%7E.o3_i.h4BK%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%221r%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..AN0M%7E.1xeqK%7E.AlJC..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%224e%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1PVg.26PYS%7E.4ha2%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%222-%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..ERGS%7E.nxOi.kGEi%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%22A%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..7EQ.1wAOa%7E.lFP..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221F%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..D2fB%7E.GK37.Wq-x%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%224%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..ud.1w58e%7E.4E8..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%2223%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1SN7.26PHG%7E.4WKq%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%224%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1MGE.26Pd8%7E.4sKo%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%226%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..9_-e%7E.26Jni%7E.3UtA..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%2214%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..BX14%7E.nuAS.hH4K%7E..45jd",
+  "templateSelected=Big+Brain+%28Histology%29&parcellationSelected=Cytoarchitectonic+Maps&regionsSelected=interpolated%238&navigation=0_0_0_1__0.3140767216682434_-0.7418519854545593_0.4988985061645508_-0.3195493221282959__1922235.5293810747__4800238.025729925_8859988.56810579_-24872710.402861338__295893.7198067633",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22COLIN_V25_RIGHT_NG_SPLIT_HEMISPHERE%22%3A%221l%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..nF8F.3cAJG~.2DEmU~..ndB",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%223l%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..6Vx.1w9b0%7E.e45..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_RIGHT_NG_SPLIT_HEMISPHERE%22%3A%221P%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..7QTa.3G7yR~.1olaD~..ndB",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%2221%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2HWqK.2wfeK%7E.2Ws-i..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%221m%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..DdeY%7E.nbr0.kciC%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%221B%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2ArGG.2zD8O%7E.2f_a8..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%224a%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2xm.1w6aq%7E.E-t..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22U%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..1CKBK%7E.59_Yv%7E.drVe%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%22y%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..BX14%7E.nuAS.hH4K%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%2219%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..vunb%7E.2172V%7E.2c-Jb..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%22y%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..BX14%7E.nuAS.hH4K%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%226%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..dcWK%7E.5DmVI%7E.D4nR..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22u%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..1hxCH%7E.1h5II%7E.m6h7%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%224I%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1OT1.26Pba%7E.4lCs%7E..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%225J%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..ZPW_%7E.nk3a.ps5q%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-43229665.07177034_8982057.416267931_30257655.50239235__1071975.4977029096&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.eu%2Fprecomputed%2FJuBrain%2F17%2Fcolin27%2Fpmaps%2FIFS_ifj2_l_N10_nlin2Stdcolin27_2.2_publicDOI_86b9e91d54b288198f3604075196534f.nii.gz",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%221-%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1Q_Z.26PQe%7E.4c0G%7E..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%224a%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2xm.1w6aq%7E.E-t..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=DiFuMo+Atlas+%28512+dimensions%29",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%221q%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..YPRG%7E.jAnO.sg_O%7E..2Ul9",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%225H%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..YEPK%7E.jAF0.suUS%7E..2Ul9",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221b%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..15nlV%7E.3s8_i.9Dyq%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%223j%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2AX2W.2zcYy%7E.2fqkS..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%225G%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..0.1w4W0%7E.0..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__35559980.33431661_-19616027.531956732_46307112.42215666__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_PoC-PrC_2.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%224H%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1OT1.26Pba%7E.4lCs%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%221s%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..9yuf%7E.26OmS%7E.3Ngp..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%225H%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..YEPK%7E.jAF0.suUS%7E..2Ul9 ",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__37380591.209646046_27002333.72228703_24475884.869700506__1071975.4977029096&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.eu%2Fprecomputed%2FJuBrain%2F17%2Ficbm152casym%2Fpmaps%2FIFS_ifs1_r_N10_nlin2MNI152ASYM2009C_2.2_publicDOI_144113cffdeb98e2b16d713a6b7502e4.nii.gz",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221o%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..1eWeV%7E.4oetO%7E.mQGR%7E..45jd",
+  "templateSelected=Big+Brain+%28Histology%29&parcellationSelected=Cytoarchitectonic+Maps&regionsSelected=interpolated%236&navigation=0_0_0_1__0.3140767216682434_-0.7418519854545593_0.4988985061645508_-0.3195493221282959__1922235.5293810747__19474749.851130597_7932494.322090598_3511322.3418121324__295893.7198067633",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%221k%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..ZUOO%7E.ni50.pm0m%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%2225%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2HWqK.2wfeK%7E.2Ws-i..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%221s%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..AN0M%7E.1xeqK%7E.AlJC..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%222-%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..Bh0m%7E.nyLe.hCkC%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221c%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..Fo8S%7E.3HLDu%7E.1ojWn%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221W%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..3HHdv%7E.135pg%7E.OL9S%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%223a%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2AkEG.2zN4K%7E.2fwny..45jd",
+  "templateSelected=Big+Brain+%28Histology%29&parcellationSelected=Cytoarchitectonic+Maps&regionsSelected=interpolated%2319&navigation=0_0_0_1__0.3140767216682434_-0.7418519854545593_0.4988985061645508_-0.3195493221282959__1922235.5293810747__-3219409.8119737757_-35998463.674534306_32696398.317760143__295893.7198067633",
+  "templateSelected=Big+Brain+%28Histology%29&parcellationSelected=Cytoarchitectonic+Maps&regionsSelected=interpolated%234&navigation=0_0_0_1__0.3140767216682434_-0.7418519854545593_0.4988985061645508_-0.3195493221282959__1922235.5293810747__4363383.768074982_836825.2022554543_4887116.718596431__295893.7198067633",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%221B%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2ArGG.2zD8O%7E.2f_a8..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%223K%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..Bqe4%7E.o0HC.h8RC%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%2222%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..533.1w8DW%7E.SRV..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%223y%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1QVh.26PTm%7E.4dte%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__43888619.854721546_-48794188.861985475_9509685.230024219__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_IP-MT_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Long%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__37914141.41414142_-29843344.155844152_19024981.96248196__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FDWM_atlas%2Fright-hemisphere%2FProbability_Maps%2FArcuate_Right.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%2256%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1k_.1w5k8%7E.8V7..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__26134653.343202576_51316308.90698248_5655378.731803611__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_RMF-SF_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221x%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2xW7y%7E.LS11%7E.Dixo%7E..2-zd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__24057215.222756192_-1166969.7363294065_54117093.12897779__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_CMF-SF_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%222V%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..abJm%7E.jBZO.bkf2%7E..2Ul9",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22COLIN_V25_RIGHT_NG_SPLIT_HEMISPHERE%22%3A%22_%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..D3uo%7E.GT_m.aV30%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%224h%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..5y-.1w92e%7E.ZNK..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%2219%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2Amey.2zJqW%7E.2fy1e..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%223h%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2AfAq.2zTQa%7E.2fuLi..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%22X%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2HTuq.2qk10%7E.2ajvG..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%2227%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..5gX.1w8nS%7E.X2B..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-28838432.91296257_-70497725.8631383_34441148.09454897__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_IP-SP_0.nii",
+  "templateSelected=Big+Brain+%28Histology%29&parcellationSelected=Cytoarchitectonic+Maps&regionsSelected=interpolated%2320&navigation=0_0_0_1__0.3140767216682434_-0.7418519854545593_0.4988985061645508_-0.3195493221282959__1922235.5293810747__-4399436.577488743_-36706103.55586253_15113373.95551695__295893.7198067633",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__44162968.51574212_8570464.767616183_17187931.034482762__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_Op-PrC_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%223j%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2HFZe.2r028%7E.2aZzK..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%222-%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..ERGS%7E.nxOi.kGEi%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%2222%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1SN7.26PHG%7E.4WKq%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_RIGHT_NG_SPLIT_HEMISPHERE%22%3A%22_%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..F4Rm%7E.FqUI.aApq%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%225J%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..YUwW%7E.jAMq.sZim%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%224X%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..4kK.1w7yK%7E.Q9b..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..0.1w4W0~.16de..16de",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%223J%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..Bqe4%7E.o0HC.h8RC%7E..45jd",
+  "templateSelected=Big+Brain+(Histology)&parcellationSelected=Grey%2FWhite+matter",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22COLIN_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221P%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..D8jS~.3MdtH~.1qixc~..ndB",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%2223%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1SN7.26PHG%7E.4WKq%7E..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%22M%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2HaUi.2we4K%7E.2WuOm..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%221u%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1VBr.26Or8%7E.4Ido%7E..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%2218%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1k_.1w5k8%7E.8V7..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221I%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..r7Tj%7E.5TEyU%7E.1LmLK..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%2218%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1k_.1w5k8%7E.8V7..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221X%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..F4Rm%7E.FqUI.aApq%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%223E%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..BMdc%7E.nple.hLS0%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%221g%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..6Vx.1w9b0%7E.e45..3q90",
+  "templateSelected=Big+Brain+%28Histology%29&parcellationSelected=Cytoarchitectonic+Maps&regionsSelected=interpolated%2321&navigation=0_0_0_1__0.3140767216682434_-0.7418519854545593_0.4988985061645508_-0.3195493221282959__1922235.5293810747__-4455614.161381434_-44097378.663157195_28855803.473491605__295893.7198067633",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-39672754.94672755_-43623033.992897004_44527904.616945714__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_SP-SM_0.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%223y%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..42a.1w7Py%7E.LfS..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%224g%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..6Eu.1w9Jq%7E.bip..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-29944625.61143861_51949767.96688825_-8936974.789915964__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_LOF-RMF_1.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%223K%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..Ed94%7E.o06G.kAl8%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%22y%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..EEyo%7E.nsUe.kLoy%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22Q%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2pLGe%7E.65Ez%7E.hYbG%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%22d%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..CNhW%7E.oDtW.gtfm%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22COLIN_V25_RIGHT_NG_SPLIT_HEMISPHERE%22%3A%22U%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..Jjic.3P2WY~.1w7Ew~..ndB",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22COLIN_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22c%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..pOEl~.3Zn9C~.1zLqf~..ndB",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221X%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..121mv%7E.2dIqq%7E.458ts..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%2255%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1KA.1w5RO%7E.6Lw..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%225%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..ud.1w58e%7E.4E8..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%222t%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..BzuQ%7E.o3_i.h4BK%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%221x%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1LhX.26Pba%7E.4u4W%7E..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%224H%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..29D.1w60u%7E.AfP..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__40553953.93783218_17772990.819777727_27986390.72314383__1071975.4977029096&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.eu%2Fprecomputed%2FJuBrain%2F17%2Ficbm152casym%2Fpmaps%2FIFS_ifj1_r_N10_nlin2MNI152ASYM2009C_2.2_publicDOI_00c617b20263b7e98ba4f25e5cf11a1f.nii.gz",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22j%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2OQwQ%7E.Wbw6.ZQ1X..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Long%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__24284226.397587553_20110020.876826733_-18368754.349338897__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FDWM_atlas%2Fright-hemisphere%2FProbability_Maps%2FUncinate_Right.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%225I%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..acOg%7E.jAXm.bhQy%7E..2Ul9",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B\"jubrain+mni152+v18+right\"%3A\"U\"%7D",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%22v%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..ZFpa%7E.nmPa.q1VW%7E..2Ul9",
+  "templateSelected=Big+Brain+(Histology)&parcellationSelected=BigBrain+Cortical+Layers+Segmentation",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22d%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..35qFY%7E.HhhV.G-rv..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__52163167.76007497_-36042361.76194939_4731490.159325197__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_MT-ST_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%22z%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..EEyo%7E.nsUe.kLoy%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__27661747.73289366_-39409810.38746908_53495383.34707339__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_PoC-SP_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%223e%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..Ezks%7E.o8wK.j-vO%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%222%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..D46o%7E.GTX0.aSQO%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%229%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..9zzE%7E.26MH4%7E.3RIH..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221P%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..A4Nm%7E.4ZMQ3%7E.28yjn..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__42928774.92877495_-43420702.754036084_40598290.59829061__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_IP-SM_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%222w%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2HN6S.2qtNS%7E.2aeuy..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%226%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..ANz8%7E.1xd2G%7E.Ao-c..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Long%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__20714547.118023783_-21787282.708142728_572735.5901189446__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FDWM_atlas%2Fright-hemisphere%2FProbability_Maps%2FFornix_Right.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%229%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..15OEx%7E.5J3Wp%7E.1YIjw..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%22u%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..ZKgq%7E.nlUC.pxu8%7E..2Ul9",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-44802912.62135923_-48078640.77669904_12375728.155339807__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_MT-SM_0.nii",
+  "templateSelected=Big+Brain+%28Histology%29&parcellationSelected=Cytoarchitectonic+Maps&regionsSelected=interpolated%2324&navigation=0_0_0_1__0.3140767216682434_-0.7418519854545593_0.4988985061645508_-0.3195493221282959__1922235.5293810747__-3185950.235648434_3919066.7112473394_-8346900.275603936__295893.7198067633",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%22R%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..246Ye.2-q7u%7E.2qikC..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%22R%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..246Ye.2-q7u%7E.2qikC..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__44008083.140877604_21172055.427251726_25098922.247882992__1071975.4977029096&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.eu%2Fprecomputed%2FJuBrain%2F17%2Fcolin27%2Fpmaps%2FIFS_ifs2_r_N10_nlin2Stdcolin27_2.2_publicDOI_fa175bc55a78d67a6b90011fecd7ade5.nii.gz",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__38368191.721132874_-73057734.20479304_5811546.840958595__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_IP-LO_0.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%22d%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..CNhW%7E.oDtW.gtfm%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__24786320.897785872_2593114.9529875815_-21704124.962086745__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_MOF-ST_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1iNg0~.1fJc4~.mE5f~..1HTs&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%222-%22%7D",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%224F%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2HRjm.2qnDi%7E.2aiDS..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%22z%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..Bh0m%7E.nyLe.hCkC%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%221q%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..ZPW_%7E.nk3a.ps5q%7E..2Ul9",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%221g%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1U30.26P3C%7E.4ObC%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%22l%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..Ezks%7E.o8wK.j-vO%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%224I%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1OT1.26Pba%7E.4lCs%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221T%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2f_6l%7E.Dh6k.2QAOP..45jd",
+  "parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&templateSelected=MNI+Colin+27&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-40700000_-39100000_56100000__200000&regionsSelected=252&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FJuBrain%2Fv2.2c%2FPMaps%2FPSC_2.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%2228%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..533.1w8DW%7E.SRV..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%228%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..6mG.1w9sC%7E.gS4..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__37153098.262898624_-43050427.79362199_44651283.38086596__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_SP-SM_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%224Z%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..9_-e%7E.26Jni%7E.3UtA..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%221o%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..aaCg%7E.jCXu.bnus%7E..2Ul9",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221D%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2FlIz%7E.2_6f2%7E.2Simj..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_RIGHT_NG_SPLIT_HEMISPHERE%22%3A%22U%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..OB2R.3JyT0~.1vffG~..ndB",
+  "templateSelected=Big+Brain+%28Histology%29&parcellationSelected=Cytoarchitectonic+Maps&regionsSelected=interpolated%2323&navigation=0_0_0_1__0.3140767216682434_-0.7418519854545593_0.4988985061645508_-0.3195493221282959__1922235.5293810747__-11566855.850716649_15797100.302998856_42172031.472206146__295893.7198067633   ",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__25822927.71632065_54094332.47461736_-10567889.074102134__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_LOF-RMF_1.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=DiFuMo+Atlas+%2864+dimensions%29",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%2220%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2PHl0.2r5J8%7E.2SHoC..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%2225%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2PHlS.2r5Je%7E.2SHo4..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%22V%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..24C3m.2-gDW%7E.2qdoa..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%22A%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1VBr.26Or8%7E.4Ido%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%2227%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1SpG.26PE8%7E.4UQK%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-35829796.26485569_-4098684.2105263323_48480263.157894745__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_CMF-PrC_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%221r%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..9zzE%7E.26MH4%7E.3RIH..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22J%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..3PZB5%7E.Qp1i%7E.yV1M%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=DiFuMo+Atlas+%28256+dimensions%29",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%223y%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..42a.1w7Py%7E.LfS..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%2225%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2HWqK.2wfeK%7E.2Ws-i..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%221o%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..ZNsu%7E.l1JK.aGyo%7E..2Ul9",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22p%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..1TW9c%7E.1Jm-n%7E.hnJd%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Long%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-42389209.75609756_-27466634.146341458_16691570.73170732__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FDWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2FArcuate_Left.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%2210%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2vSzF%7E.1ljXk%7E.wdlp%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22R%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..3Lg4Z%7E.1ajgd%7E.2KaGh..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-37988939.74065599_-14141495.041952714_11507932.875667438__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_PoC-Ins_0.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1eq5c~.1ZSV4~.gHn1~..1HmA&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%222-%22%7D",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%224X%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1RwH.26PKO%7E.4YF0%7E..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%22M%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2HaUi.2we4K%7E.2WuOm..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%224e%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1PVg.26PYS%7E.4ha2%7E..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%223k%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..6mG.1w9sC%7E.gS4..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__12109082.813891366_-41632680.3205699_36048530.72128227__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_PoCi-PrCu_2.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%2214%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..BX14%7E.nuAS.hH4K%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__30726322.48682058_-6315488.093073979_49600163.606616974__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_CMF-PrC_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%22M%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2PL0q.2r45W%7E.2SKKa..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%227%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1UqM.26Ovq%7E.4Kdm%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%221t%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..9yuf%7E.26OmS%7E.3Ngp..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..U-.1w1ri~.2Fq5..16de",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-45323691.4600551_-19717630.85399449_27943526.170798898__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_PrC-SM_0.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%224X%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..4kK.1w7yK%7E.Q9b..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221C%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2w5Vj%7E.3irqF%7E.2P_Gr..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%224G%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2Amey.2zJqW%7E.2fy1e..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22G%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW.._PbS%7E.3T7dn%7E.2Dwe7%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%2226%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1SpG.26PE8%7E.4UQK%7E..3q90",
+  "templateSelected=MNI%20Colin%2027&parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2qFHc.3WhR.l89T~..2kDL&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.eu%2Fprecomputed%2FJuBrain%2F17%2Fcolin27%2Fpmaps%2FArea-TI2_r_N10_nlin2Stdcolin27_5.1_publicDOI_42dd664fb5c8690e7c149fcb0d821a0e.nii.gz",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%224F%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2Amey.2zJqW%7E.2fy1e..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%224d%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1O_e.26Pa0%7E.4jP4%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-52795703.51401337_-10398563.095746204_27672428.51045668__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_PoC-PrC_0.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%2257%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2Amey.2zJqW%7E.2fy1e..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%221n%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..BBwc%7E.nkwG.hPrG%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__37929057.88876277_23993757.09421113_24460272.417707145__1071975.4977029096&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.eu%2Fprecomputed%2FJuBrain%2F17%2Fcolin27%2Fpmaps%2FIFS_ifs1_r_N10_nlin2Stdcolin27_2.2_publicDOI_baf7f7a7c7d00a2044f409b92b78b926.nii.gz",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%221_%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..42a.1w7Py%7E.LfS..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%22k%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..Ed94%7E.o06G.kAl8%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Long%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__37914141.41414142_-29843344.155844152_19024981.96248196__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FDWM_atlas%2Fright-hemisphere%2FProbability_Maps%2FArcuate_Right.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%222t%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..Eodw%7E.o4bm.k5Ii%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221u%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..3FnSS%7E.1mjIQ%7E.aEu0..2-zd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221r%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..e7-N%7E.4_5ES%7E.Anmr..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%22B%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..3cWaf%7E.dyXQ%7E.y16D..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__31924155.28396836_-60861610.35226457_-9854421.279654935__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_Fu-LO_1.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%223h%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2AfAq.2zTQa%7E.2fuLi..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%221n%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..Dr3w%7E.nhsy.kX3G%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%221m%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..BBwc%7E.nkwG.hPrG%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221P%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..Fo8S~.3HLDu~.1ojWn~..ndB",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%2225%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2PHlS.2r5Je%7E.2SHo4..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__44707371.225577265_9416370.633510947_27168590.882178783__1071975.4977029096&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.eu%2Fprecomputed%2FJuBrain%2F17%2Fcolin27%2Fpmaps%2FIFS_ifj2_r_N10_nlin2Stdcolin27_2.2_publicDOI_346dca14e393e290b70807d4e30cd835.nii.gz",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__43590116.62151018_12108670.043585807_26892390.15196137__1071975.4977029096&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.eu%2Fprecomputed%2FJuBrain%2F17%2Ficbm152casym%2Fpmaps%2FIFS_ifj2_r_N10_nlin2MNI152ASYM2009C_2.2_publicDOI_a988cbde839a0399e767be7ef02cc95c.nii.gz",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%222V%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..abJm%7E.jBZO.bkf2%7E..2Ul9",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%224h%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1Tf8.26P6K%7E.4QYM%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-42624840.388959825_3251006.777330339_37158579.70729791__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_CMF-PrC_1.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Long%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-34248274.94692144_-21216427.813163474_-6111199.5753715485__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FDWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2FInferiorFrontoOccipital_Left.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-42343003.412969284_-2070753.4786033034_10107771.068521932__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_PrC-Ins_0.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%223G%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..5y-.1w92e%7E.ZNK..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221y%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..1I-0R%7E.22bix.1CyL8%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%223j%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2HFZe.2r028%7E.2aZzK..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+Maps+-+v2.5.1&cRegionsSelected=%7B%22MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE%22%3A%221d%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..QaW_%7E.27wgv.sU35%7E..45jd",
+  "templateSelected=Big+Brain+%28Histology%29&parcellationSelected=Cytoarchitectonic+Maps&regionsSelected=interpolated%2322&navigation=0_0_0_1__0.3140767216682434_-0.7418519854545593_0.4988985061645508_-0.3195493221282959__1922235.5293810747__-9349145.390744817_27783956.655307576_38734626.88870795__295893.7198067633",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-26408504.835110337_-10505207.041904286_54935159.93057278__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_PrC-SF_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Long%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-39192168.637784176_-42631349.96016914_-8671180.832158834__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FDWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2FInferiorLongitudinal_Left.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%227%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..6-m.1wA7O%7E.irG..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%229%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..ANz8%7E.1xd2G%7E.Ao-c..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Short+Fiber+Bundles+-+HCP",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%224%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1MGE.26Pd8%7E.4sKo%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%223h%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2HKgC.2qwM4%7E.2adEi..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%224W%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..4kK.1w7yK%7E.Q9b..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%2226%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..5ND.1w8WG%7E.Ukc..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%223G%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1TEZ.26PB0%7E.4SUu%7E..3q90",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%225%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..ud.1w58e%7E.4E8..3q90",
+  "templateSelected=Big+Brain+%28Histology%29&parcellationSelected=Cytoarchitectonic+Maps&cRegionsSelected=%7B%22v5%22%3A%221%22%7D&cNavigation=0.0.0.-W000..-GHSG.2_0PIA.zn_OK.2-8mTZ..7LIx..r15q%7E.2X38a%7E.Gx92%7E..jZZ",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%223l%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1US2.26O_W%7E.4Mco%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Long%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__39308927.09196353_-22777996.40983154_32738193.869096935__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FDWM_atlas%2Fright-hemisphere%2FProbability_Maps%2FArcuate_Anterior_Right.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%2227%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1SpG.26PE8%7E.4UQK%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__30727572.31783229_7292200.659099221_-24747345.294763826__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fright-hemisphere%2FProbability_Maps%2Frh_LOF-ST_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Long%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-20803546.879296124_-36580285.94995877_-10145861.974154532__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FDWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2FCingulum_Temporal_Left.nii",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%223k%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..6mG.1w9sC%7E.gS4..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__38875694.35035902_22074041.45779705_32925552.093212306__1071975.4977029096&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.eu%2Fprecomputed%2FJuBrain%2F17%2Ficbm152casym%2Fpmaps%2FIFS_ifs3_r_N10_nlin2MNI152ASYM2009C_2.2_publicDOI_e664da66c465b00af07d0d53e6300b17.nii.gz",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%222-%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..Bh0m%7E.nyLe.hCkC%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+left%22%3A%225%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1MqM.26Pd8%7E.4qaI%7E..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%2230%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..E2CO%7E.nnMW.kRQK%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%22k%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..BzuQ%7E.o3_i.h4BK%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+left%22%3A%221p%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..ZNsu%7E.l1JK.aGyo%7E..2Ul9",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%223e%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..CFOu%7E.oAn4.gxpO%7E..45jd",
+  "templateSelected=MNI+Colin+27&parcellationSelected=Cytoarchitectonic+Maps+-+v1.18&cRegionsSelected=%7B%22jubrain+colin+v18+right%22%3A%224c%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..2Yr.1w6Je%7E.CqH..3q90",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Long%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-29901044.899648257_15264845.851438016_-17445944.547899857__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FDWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2FUncinate_Left.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%221q%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..YPRG%7E.jAnO.sg_O%7E..2Ul9",
+  "templateSelected=Big+Brain+%28Histology%29&parcellationSelected=Cytoarchitectonic+Maps&navigation=0_0_0_1__0.7989629507064819_0.09687352180480957_-0.0713578313589096_-0.5892213582992554__1922235.5293810747__311768_-54882876_4142912__350000&regionsSelected=v2%231",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Fibre%20Bundle%20Atlas%20-%20Short%20Bundle&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-40639583.333333336_-76233333.33333334_-11417708.333333336__484084&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FFiber_Bundle%2FSWM_atlas%2Fleft-hemisphere%2FProbability_Maps%2Flh_Fu-LO_0.nii",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%22z%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..EEyo%7E.nsUe.kLoy%7E..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%224F%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..DMVW..2HRjm.2qnDi%7E.2aiDS..45jd",
+  "templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=Cytoarchitectonic+maps+-+v1.18&cRegionsSelected=%7B%22jubrain+mni152+v18+right%22%3A%223%22%7D&cNavigation=0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..1MGE.26Pd8%7E.4sKo%7E..3q90",
+  "templateSelected=Big+Brain+%28Histology%29&parcellationSelected=Cytoarchitectonic+Maps&cRegionsSelected=%7B%22v3v%22%3A%221%22%7D&cNavigation=0.0.0.-W000..-DAqw.2_PGSc._ShZn.2-8GXE..7LIx..Echz.3Dv8q%7E.7gwp%7E..jZZ"
+]
diff --git a/deploy/bkwdCompat/urlState.js b/deploy/bkwdCompat/urlState.js
new file mode 100644
index 0000000000000000000000000000000000000000..f902a8a9f32a10dd843a0d0e4e561db18f28c7e3
--- /dev/null
+++ b/deploy/bkwdCompat/urlState.js
@@ -0,0 +1,242 @@
+// this module is suppose to rewrite state stored in query param
+// and convert it to path based url
+const separator = '.'
+const waxolmObj = {
+  aId: 'minds/core/parcellationatlas/v1.0.0/522b368e-49a3-49fa-88d3-0870a307974a',
+  id: 'minds/core/referencespace/v1.0.0/d5717c4a-0fa1-46e6-918c-b8003069ade8',
+  parc: {
+    'Waxholm Space rat brain atlas v3': {
+      id: 'minds/core/parcellationatlas/v1.0.0/ebb923ba-b4d5-4b82-8088-fa9215c2e1fe'
+      },
+    'Whole Brain (v2.0)': {
+      id: 'minds/core/parcellationatlas/v1.0.0/2449a7f0-6dd0-4b5a-8f1e-aec0db03679d'
+    },
+    'Waxholm Space rat brain atlas v2': {
+      id: 'minds/core/parcellationatlas/v1.0.0/2449a7f0-6dd0-4b5a-8f1e-aec0db03679d'
+      },
+    'Waxholm Space rat brain atlas v1': {
+      id: 'minds/core/parcellationatlas/v1.0.0/11017b35-7056-4593-baad-3934d211daba'
+    },
+  }
+}
+
+const allenObj = {
+  aId: 'juelich/iav/atlas/v1.0.0/2',
+  id: 'minds/core/referencespace/v1.0.0/265d32a0-3d84-40a5-926f-bf89f68212b9',
+  parc: {
+    'Allen Mouse Common Coordinate Framework v3 2017': {
+      id: 'minds/core/parcellationatlas/v1.0.0/05655b58-3b6f-49db-b285-64b5a0276f83'
+    },
+    'Allen Mouse Brain Atlas': {
+      id: 'minds/core/parcellationatlas/v1.0.0/39a1384b-8413-4d27-af8d-22432225401f'
+    },
+    'Allen Mouse Common Coordinate Framework v3 2015': {
+      id: 'minds/core/parcellationatlas/v1.0.0/39a1384b-8413-4d27-af8d-22432225401f'
+    },
+  }
+}
+
+const templateMap = {
+  'Big Brain (Histology)': {
+    aId: 'juelich/iav/atlas/v1.0.0/1',
+    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'
+      },
+      'Cortical Layers Segmentation': {
+        id: 'juelich/iav/atlas/v1.0.0/3'
+      },
+      'Grey/White matter': {
+        id: 'juelich/iav/atlas/v1.0.0/4'
+      }
+    }
+  },
+  'MNI 152 ICBM 2009c Nonlinear Asymmetric': {
+    aId: 'juelich/iav/atlas/v1.0.0/1',
+    id: 'minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2',
+    parc: {
+      'Cytoarchitectonic Maps - v2.5.1': {
+        id: 'minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-26'
+      },
+      'Short Fiber Bundles - HCP': {
+        id: 'juelich/iav/atlas/v1.0.0/79cbeaa4ee96d5d3dfe2876e9f74b3dc3d3ffb84304fb9b965b1776563a1069c'
+      },
+      'Cytoarchitectonic maps - v1.18': {
+        id: 'juelich/iav/atlas/v1.0.0/8'
+      },
+      'Long Bundle': {
+        id: 'juelich/iav/atlas/v1.0.0/5'
+      },
+      'fibre bundle short': {
+        id: 'juelich/iav/atlas/v1.0.0/6'
+      },
+      'DiFuMo Atlas (64 dimensions)': {
+        id: 'minds/core/parcellationatlas/v1.0.0/d80fbab2-ce7f-4901-a3a2-3c8ef8a3b721'
+      },
+      'DiFuMo Atlas (128 dimensions)': {
+        id: 'minds/core/parcellationatlas/v1.0.0/73f41e04-b7ee-4301-a828-4b298ad05ab8'
+      },
+      'DiFuMo Atlas (256 dimensions)': {
+        id: 'minds/core/parcellationatlas/v1.0.0/141d510f-0342-4f94-ace7-c97d5f160235'
+      },
+      'DiFuMo Atlas (512 dimensions)': {
+        id: 'minds/core/parcellationatlas/v1.0.0/63b5794f-79a4-4464-8dc1-b32e170f3d16'
+      },
+      'DiFuMo Atlas (1024 dimensions)': {
+        id: 'minds/core/parcellationatlas/v1.0.0/12fca5c5-b02c-46ce-ab9f-f12babf4c7e1'
+      },
+    },
+  },
+  'MNI Colin 27': {
+    aId: 'juelich/iav/atlas/v1.0.0/1',
+    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'
+      }
+    }
+  },
+  'Waxholm Space rat brain MRI/DTI': waxolmObj,
+  'Waxholm Rat V2.0': waxolmObj,
+  'Allen Mouse Common Coordinate Framework v3': allenObj,
+  'Allen Mouse': allenObj
+}
+
+const encodeId = id => id.replace(/\//g, ':')
+
+const WARNING_STRINGS = {
+  PREVIEW_DSF_PARSE_ERROR: 'Preview dataset files cannot be processed properly.',
+  REGION_SELECT_ERROR: 'Region selected cannot be processed properly.',
+  TEMPLATE_ERROR: 'Template not found.',
+}
+
+module.exports = (query, _warningCb) => {
+  const {
+    standaloneVolumes,
+    niftiLayers, // deprecating - check if anyone calls this url
+    pluginStates,
+    previewingDatasetFiles,
+
+    templateSelected,
+    parcellationSelected,
+    regionsSelected, // deprecating - check if any one calls this url
+    cRegionsSelected,
+
+    navigation, // deprecating - check if any one calls this endpoint
+    cNavigation,
+  } = query || {}
+
+  if (Object.keys(query).length === 0) return null
+
+  /**
+   * nb: it is absolutely imperative that warningCb are not called with dynamic data.
+   * Since it is injected as param in 
+   */
+  const warningCb = arg => {
+    if (!_warningCb) return
+    if (Object.values(WARNING_STRINGS).includes(arg)) _warningCb(arg)
+  }
+
+  if (navigation) console.warn(`navigation has been deprecated`)
+  if (regionsSelected) console.warn(`regionSelected has been deprecated`)
+  if (niftiLayers) console.warn(`nifitlayers has been deprecated`)
+
+  // pluginStates from query param were separated by __
+  // convert to uri component encoded JSON array
+  // to avoid potentially issues (e.g. url containing __, which is very possible)
+
+  const plugins = pluginStates && pluginStates.split('__')
+  const searchParam = new URLSearchParams()
+
+
+  // common search param & path
+  let nav, dsp, r
+  if (cNavigation) nav = `/@:${encodeURI(cNavigation)}`
+  if (previewingDatasetFiles) {
+    try {
+      const parsedDsp = JSON.parse(previewingDatasetFiles)
+      if (Array.isArray(parsedDsp)) {
+        if (parsedDsp.length === 1) {
+          const { datasetId, filename } = parsedDsp[0]
+          dsp = `/dsp:${encodeId(datasetId)}::${encodeURI(filename)}`
+        } else {
+          searchParam.set(`previewingDatasetFiles`, previewingDatasetFiles)
+        }
+      }
+    } catch (_e) {
+      warningCb(WARNING_STRINGS.PREVIEW_DSF_PARSE_ERROR)
+      // parsing preview dataset error
+      // ignore preview dataset query param
+    }
+  }
+  if (plugins && plugins.length > 0) {
+    searchParam.set(`pl`, JSON.stringify(plugins))
+  }
+  if (niftiLayers) searchParam.set(`niftiLayers`, niftiLayers)
+  if (cRegionsSelected) {
+    try {
+      (() => {
+        const parsedRS = JSON.parse(cRegionsSelected)
+        if (Object.keys(parsedRS).length > 1) {
+          searchParam.set('cRegionsSelected', cRegionsSelected)
+          return
+        }
+        for (const ngId in parsedRS) {
+          const encodedIdArr = parsedRS[ngId]
+          const encodedRArr = encodedIdArr.split(separator)
+          if (encodedRArr.length > 0) {
+            if (encodedRArr.length > 1) {
+              searchParam.set('cRegionsSelected', cRegionsSelected)
+              return
+            }
+            if (!!encodedRArr[0]) {
+              r = `/r:${encodeURI(ngId)}::${encodeURI(encodedRArr[0])}`
+            }
+          }
+        }
+      })()
+    } catch (e) {
+      warningCb(WARNING_STRINGS.REGION_SELECT_ERROR)
+      // parsing cregions selected error
+      // ignore region selected and move on
+    }
+  }
+
+  let redirectUrl = '/#'
+  if (standaloneVolumes) {
+    searchParam.set('standaloneVolumes', standaloneVolumes)
+    if (nav) redirectUrl += nav
+    if (dsp) redirectUrl += dsp
+    if (Array.from(searchParam.keys()).length > 0) redirectUrl += `/?${searchParam.toString()}`
+    return redirectUrl
+  }
+
+  if (templateSelected) {
+    if (templateMap[templateSelected]) {
+
+      const { id: t, aId: a, parc } = templateMap[templateSelected]
+      redirectUrl += `/a:${encodeId(a)}/t:${encodeId(t)}`
+      if (!parc[parcellationSelected]) {
+        warningCb(`Parcelation not found, using default.`)
+      }
+      const { id: p } = parc[parcellationSelected] || {}
+      if (p) redirectUrl += `/p:${encodeId(p)}`
+      if (r) redirectUrl += r
+      if (nav) redirectUrl += nav
+      if (dsp) redirectUrl += dsp
+      
+      if (Array.from(searchParam.keys()).length > 0) redirectUrl += `?${searchParam.toString()}`
+  
+      return redirectUrl
+    } else {
+      warningCb(WARNING_STRINGS.TEMPLATE_ERROR)
+    }
+  }
+
+  if (Array.from(searchParam.keys()).length > 0) {
+    redirectUrl += `/?${searchParam.toString()}`
+    return redirectUrl
+  }
+  return `${redirectUrl}/`
+}
diff --git a/deploy/bkwdCompat/urlState.spec.js b/deploy/bkwdCompat/urlState.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..225bf252f5342828e75b58f20bdfc3d78626a9bd
--- /dev/null
+++ b/deploy/bkwdCompat/urlState.spec.js
@@ -0,0 +1,18 @@
+const { assert } = require('chai')
+const url = require('url')
+const arr = require('./ngLinks.json')
+const parseUrlState = require('./urlState')
+
+describe('> urlState.js', () => {
+  it('> works', () => {
+    for (const item of arr) {
+      const parsed = url.parse(`/?${item}`, true)
+      const out = parseUrlState(parsed.query)
+      
+      assert(
+        true,
+        'should not result in parsing error'  
+      )
+    }
+  })
+})
diff --git a/deploy/constants.js b/deploy/constants.js
index 4ba65d5c2aa5bda63b2a87443aadfc1d2463a337..05090af3fd5829887b0616f45fbae6cef8082dc2 100644
--- a/deploy/constants.js
+++ b/deploy/constants.js
@@ -5,11 +5,17 @@ const PUBLIC_PATH = process.env.NODE_ENV === 'production'
   ? path.join(__dirname, 'public')
   : path.join(__dirname, '..', 'dist', 'aot')
 
-const indexTemplate = fs.readFileSync(
-  path.join(PUBLIC_PATH, 'index.html'),
-  'utf-8'
-)
+let indexTemplate
+try {
 
+  indexTemplate = fs.readFileSync(
+    path.join(PUBLIC_PATH, 'index.html'),
+    'utf-8'
+  )
+  
+} catch (e) {
+  console.error(`index.html cannot be read. maybe run 'npm run build-aot' at root dir first?`)
+}
 module.exports = {
   indexTemplate
 }
diff --git a/deploy/csp/index.js b/deploy/csp/index.js
index b02e7fede98bef36b4940c360d4668083c8167bd..616a0ab03fe626d20af4c007eca18a0347e749a7 100644
--- a/deploy/csp/index.js
+++ b/deploy/csp/index.js
@@ -1,6 +1,5 @@
 const csp = require('helmet-csp')
-const express = require('express')
-const crypto = require('crypto')
+const bodyParser = require('body-parser')
 
 let WHITE_LIST_SRC, CSP_CONNECT_SRC, SCRIPT_SRC
 
@@ -49,73 +48,82 @@ const connectSrc = [
   ...CSP_CONNECT_SRC
 ]
 
-module.exports = (app) => {
-  app.use((req, res, next) => {
-    if (req.path === '/') res.locals.nonce = crypto.randomBytes(16).toString('hex')
-    next()
-  })
+module.exports = {
+  middelware: (req, res, next) => {
 
-  app.use(csp({
-    directives: {
-      defaultSrc: [
-        ...defaultAllowedSites,
-        ...WHITE_LIST_SRC
-      ],
-      styleSrc: [
-        ...defaultAllowedSites,
-        'stackpath.bootstrapcdn.com/bootstrap/4.3.1/',
-        'use.fontawesome.com/releases/v5.8.1/',
-        "'unsafe-inline'", // required for angular [style.xxx] bindings
-        ...WHITE_LIST_SRC
-      ],
-      fontSrc: [
-        "'self'",
-        'use.fontawesome.com/releases/v5.8.1/',
-        ...WHITE_LIST_SRC
-      ],
-      connectSrc: [
-        ...defaultAllowedSites,
-        ...connectSrc,
-        ...WHITE_LIST_SRC
-      ],
-      imgSrc: [
-        "'self'",
-        "hbp-kg-dataset-previewer.apps.hbp.eu/v2/"
-      ],
-      scriptSrc:[
-        "'self'",
-        'code.jquery.com', // plugin load external library -> jquery v2 and v3
-        'cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/', // plugin load external library -> web components
-        'cdnjs.cloudflare.com/ajax/libs/d3/', // plugin load external lib -> d3
-        'cdn.jsdelivr.net/npm/vue@2.5.16/', // plugin load external lib -> vue 2
-        'cdn.jsdelivr.net/npm/preact@8.4.2/', // plugin load external lib -> preact
-        'unpkg.com/react@16/umd/', // plugin load external lib -> react
-        'unpkg.com/kg-dataset-previewer@1.1.5/', // preview component
-        'cdnjs.cloudflare.com/ajax/libs/mathjax/', // math jax
-        (req, res) => res.locals.nonce ? `'nonce-${res.locals.nonce}'` : null,
-        ...SCRIPT_SRC,
-        ...WHITE_LIST_SRC,
-        ...defaultAllowedSites
-      ],
-      reportUri: CSP_REPORT_URI || '/report-violation'
-    },
-    reportOnly
-  }))
+    const permittedCsp = (req.session && req.session.permittedCsp) || {}
+    const userConnectSrc = []
+    const userScriptSrc = []
+    for (const key in permittedCsp) {
+      userConnectSrc.push(
+        ...(permittedCsp[key]['connect-src'] || []),
+        ...(permittedCsp[key]['connectSrc'] || [])
+      )
+      userScriptSrc.push(
+        ...(permittedCsp[key]['script-src'] || []),
+        ...(permittedCsp[key]['scriptSrc'] || [])
+      )
+    }
+    csp({
+      directives: {
+        defaultSrc: [
+          ...defaultAllowedSites,
+          ...WHITE_LIST_SRC
+        ],
+        styleSrc: [
+          ...defaultAllowedSites,
+          'stackpath.bootstrapcdn.com/bootstrap/4.3.1/',
+          'use.fontawesome.com/releases/v5.8.1/',
+          "'unsafe-inline'", // required for angular [style.xxx] bindings
+          ...WHITE_LIST_SRC
+        ],
+        fontSrc: [
+          "'self'",
+          'use.fontawesome.com/releases/v5.8.1/',
+          ...WHITE_LIST_SRC
+        ],
+        connectSrc: [
+          ...userConnectSrc,
+          ...defaultAllowedSites,
+          ...connectSrc,
+          ...WHITE_LIST_SRC
+        ],
+        imgSrc: [
+          "'self'",
+          "hbp-kg-dataset-previewer.apps.hbp.eu/v2/"
+        ],
+        scriptSrc:[
+          "'self'",
+          ...userScriptSrc,
+          'code.jquery.com', // plugin load external library -> jquery v2 and v3
+          'cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/', // plugin load external library -> web components
+          'cdnjs.cloudflare.com/ajax/libs/d3/', // plugin load external lib -> d3
+          'cdn.jsdelivr.net/npm/vue@2.5.16/', // plugin load external lib -> vue 2
+          'cdn.jsdelivr.net/npm/preact@8.4.2/', // plugin load external lib -> preact
+          'unpkg.com/react@16/umd/', // plugin load external lib -> react
+          'unpkg.com/kg-dataset-previewer@1.2.0/', // preview component
+          'cdnjs.cloudflare.com/ajax/libs/mathjax/', // math jax
+          'https://unpkg.com/three-surfer@0.0.10/dist/bundle.js', // for threeSurfer (freesurfer support in browser)
+          (req, res) => res.locals.nonce ? `'nonce-${res.locals.nonce}'` : null,
+          ...SCRIPT_SRC,
+          ...WHITE_LIST_SRC,
+          ...defaultAllowedSites
+        ],
+        reportUri: CSP_REPORT_URI || '/report-violation'
+      },
+      reportOnly
+    })(req, res, next)
 
-  if (!CSP_REPORT_URI) {
-    app.post(
-      '/report-violation',
-      express.json({
-        type: ['json', 'application/csp-report']
-      }),
-      (req, res) => {
-        if (req.body) {
-          console.warn(`CSP Violation: `, req.body)
-        } else {
-          console.warn(`CSP Violation: no data received!`)
-        }
-        res.status(204).end()
+  },
+  bootstrapReportViolation: app => {
+    app.post('/report-violation', bodyParser.json({
+      type: ['json', 'application/csp-report']
+    }), (req, res) => {
+      if (req.body) {
+        console.warn(`CSP Violation: `, req.body)
+      } else {
+        console.warn(`CSP Violation: no data received!`)
       }
-    )
+    })
   }
-}
\ No newline at end of file
+}
diff --git a/deploy/csp/index.spec.js b/deploy/csp/index.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..93a478e25b498521f32c8f833a017a1f390db8c3
--- /dev/null
+++ b/deploy/csp/index.spec.js
@@ -0,0 +1,109 @@
+const express = require('express')
+const app = express()
+const { middelware: cspMiddleware } = require('./index')
+const got = require('got')
+const { expect, assert } = require('chai')
+
+const checkBaseFn = async (rules = []) => {
+
+  const resp = await got(`http://localhost:1234/`)
+  const stringifiedHeader = JSON.stringify(resp.headers)
+
+  /**
+   * expect stats.humanbrainproject.eu and neuroglancer.humanbrainproject.eu to be present
+   */
+  assert(
+    /stats\.humanbrainproject\.eu/.test(stringifiedHeader),
+    'stats.humanbrainproject.eu present in header'
+  )
+
+  assert(
+    /neuroglancer\.humanbrainproject\.eu/.test(stringifiedHeader),
+    'neuroglancer.humanbrainproject.eu present in header'
+  )
+
+  assert(
+    /content-security-policy/.test(stringifiedHeader),
+    'content-security-policy present in header'
+  )
+
+  for (const rule of rules) {
+    assert(
+      rule.test(stringifiedHeader),
+      `${rule.toString()} present in header`
+    )
+  }
+}
+
+describe('> csp/index.js', () => {
+  let server, permittedCsp
+  const middleware = (req, res, next) => {
+    if (!!permittedCsp) {
+      req.session = { permittedCsp }
+    }
+    next()
+  }
+  before(done => {
+    app.use(middleware)
+    app.use(cspMiddleware)
+    app.get('/', (req, res) => {
+      res.status(200).send('OK')
+    })
+    server = app.listen(1234, () => console.log(`app listening`))
+    setTimeout(() => {
+      done()
+    }, 1000);
+  })
+
+  it('> should work when session is unset', async () => {
+    await checkBaseFn()
+  })
+
+  describe('> if session and permittedCsp are both set', () => {
+    describe('> if permittedCsp is malformed', () => {
+      describe('> if permittedCsp is set to string', () => {
+        before(() => {
+          permittedCsp = 'hello world'
+        })
+        it('> base csp should work', async () => {
+          await checkBaseFn()
+        })
+      })
+
+      describe('> if permittedCsp is number', () => {
+        before(() => {
+          permittedCsp = 420
+        })
+        it('> base csp should work', async () => {
+          await checkBaseFn()
+        })
+      })
+    })
+  
+    describe('> if premittedCsp defines', () => {
+
+      before(() => {
+        permittedCsp = {
+          'foo-bar': {
+            'connect-src': [
+              'connect.int.dev'
+            ],
+            'script-src': [
+              'script.int.dev'
+            ]
+          }
+        }
+      })
+      
+      it('> csp should include permittedCsp should work', async () => {
+        await checkBaseFn([
+          /connect\.int\.dev/,
+          /script\.int\.dev/,
+        ])
+      })
+    })
+  })
+  after(done => {
+    server.close(done)
+  })
+})
\ No newline at end of file
diff --git a/deploy/datasets/index.js b/deploy/datasets/index.js
index b1897f45cb28568fcc4712c8ba878b4c15d3c2b4..4b3780d1ad2249d0643cfbeac63f4a5b8f83b910 100644
--- a/deploy/datasets/index.js
+++ b/deploy/datasets/index.js
@@ -191,6 +191,7 @@ datasetsRouter.get('/hasPreview', cacheMaxAge24Hr, async (req, res) => {
 })
 
 datasetsRouter.get('/kgInfo', checkKgQuery, cacheMaxAge24Hr, async (req, res) => {
+  return res.status(400).send('Deprecated')
   const { kgId } = req.query
   const { kgSchema } = req.query
   const { user } = req
diff --git a/deploy/datasets/query.js b/deploy/datasets/query.js
index 54e60dfef04f9ffa7a28864093f4eacbe9de39b7..4911e4f805627a38e5d08959eec2304ed752b7d9 100644
--- a/deploy/datasets/query.js
+++ b/deploy/datasets/query.js
@@ -7,29 +7,11 @@ const { getPreviewFile, hasPreview } = require('./supplements/previewFile')
 const { constants, init: kgQueryUtilInit, getUserKGRequestParam, filterDatasets, filterDatasetsByRegion } = require('./util')
 const ibc = require('./importIBS')
 const { returnAdditionalDatasets } = require('../regionalFeatures')
+const lruStore = require('../lruStore')
 
-let cachedData = null
-
-const CACHE_DATASET_FILENAME = process.env.CACHE_DATASET_FILENAME || path.join(__dirname, 'cachedKgDataset.json')
-
-fs.readFile(CACHE_DATASET_FILENAME, 'utf-8', (err, data) => {
-  /**
-   * the file may or may not be present on init
-   */
-  if (err) {
-    console.warn(`read cache failed`, err)
-    return
-  }
-
-  try {
-    cachedData = JSON.parse(data)
-  }catch (e) {
-    /**
-     * parsing saved cached json error
-     */
-    console.error(e)
-  }
-})
+const IAV_DS_CACHE_KEY = 'IAV_DS_CACHE_KEY'
+const IAV_DS_TIMESTAMP_KEY = 'IAV_DS_TIMESTAMP_KEY'
+const IAV_DS_REFRESH_TIMESTAMP_KEY = 'IAV_DS_REFRESH_TIMESTAMP_KEY'
 
 const { KG_ROOT, KG_SEARCH_VOCAB } = constants
 
@@ -57,55 +39,61 @@ const getKgQuerySingleDatasetUrl = ({ kgId }) => {
 const fetchDatasetFromKg = async ({ user } = {}) => {
 
   const { releasedOnly, option } = await getUserKGRequestParam({ user })
-
   return await new Promise((resolve, reject) => {
-    request(`${KG_QUERY_DATASETS_URL}${releasedOnly ? '&databaseScope=RELEASED' : '&databaseScope=INFERRED'}`, option, (err, resp, body) => {
-      if (err) return reject(err)
-      if (resp.statusCode >= 400) return reject(resp.statusCode)
-      try {
-        const json = JSON.parse(body)
-        return resolve(json)
-      }catch (e) {
-        console.warn(`parsing json obj error`, body)
-        reject(e)
-      }
-    })
+    request(
+      `${KG_QUERY_DATASETS_URL}${releasedOnly ? '&databaseScope=RELEASED' : ''}`,
+      {
+        timeout: 60 * 1000,
+        ...option
+      },
+      (err, resp, body) => {
+        if (err) return reject(err)
+        if (resp.statusCode >= 400) return reject(resp.statusCode)
+        resolve(body)
+      })
   })
 }
 
-const cacheData = ({ results, ...rest }) => {
-  cachedData = results
-  otherQueryResult = rest
-  fs.writeFile(CACHE_DATASET_FILENAME, JSON.stringify(results), (err) => {
-    if (err) console.error('writing cached data fail')
-  })
-  return cachedData
+const refreshCache = async () => {
+  await lruStore._initPr
+  const { store } = lruStore
+  store.set(IAV_DS_REFRESH_TIMESTAMP_KEY, new Date().toString())
+  const text = await fetchDatasetFromKg()
+  await store.set(IAV_DS_CACHE_KEY, text)
+  await store.set(IAV_DS_REFRESH_TIMESTAMP_KEY, null)
+  await store.set(IAV_DS_TIMESTAMP_KEY, new Date().toString())
 }
 
-let fetchingPublicDataInProgress = false
-let getPublicDsPr
-
 const getPublicDs = async () => {
   console.log(`fetching public ds ...`)
-
-  /**
-   * every request to public ds will trigger a refresh pull from master KG (throttled pending on resolved request)
-   */
-  if (!fetchingPublicDataInProgress) {
-    fetchingPublicDataInProgress = true
-    getPublicDsPr = fetchDatasetFromKg()
-      .then(_ => {
-        console.log(`public ds fetched!`)
-        fetchingPublicDataInProgress = false
-        getPublicDsPr = null
-        return _
-      })
-      .then(cacheData)
+  
+  await lruStore._initPr
+  const { store } = lruStore
+
+  let cachedData = await store.get(IAV_DS_CACHE_KEY)
+  if (!cachedData) {
+    await refreshCache()
+    cachedData = await store.get(IAV_DS_CACHE_KEY)
   }
 
-  if (cachedData) return Promise.resolve(cachedData)
-  if (getPublicDsPr) return getPublicDsPr
-  throw `cached Data not yet resolved, neither is get public ds defined`
+  const timestamp = await store.get(IAV_DS_TIMESTAMP_KEY)
+  const refreshTimestamp = await store.get(IAV_DS_REFRESH_TIMESTAMP_KEY)
+  
+  if (
+    new Date() - new Date(timestamp) > 1e3 * 60 * 30
+  ) {
+    if (
+      !refreshTimestamp ||
+      new Date() - new Date(refreshTimestamp) > 1e3 * 60 * 5
+    ) {
+      refreshCache()
+    }
+  }
+  if (cachedData) {
+    const { results } = JSON.parse(cachedData)
+    return Promise.resolve(results)
+  }
+  throw new Error(`cacheData not defined!`)
 }
 
 /**
@@ -113,7 +101,9 @@ const getPublicDs = async () => {
  * getting individual ds is too slow
  */
 const getDs = ({ user }) => (false && user
-    ? fetchDatasetFromKg({ user }).then(({ results }) => results)
+    ? fetchDatasetFromKg({ user })
+        .then(text => JSON.parse(text))
+        .then(({ results }) => results)
     : getPublicDs()
   ).then(async datasets => {
     
diff --git a/deploy/datasets/testData/bigbrain.js b/deploy/datasets/testData/bigbrain.js
index ca8e64871a35dcdc8685f64faf14c1a982c23868..6e5c5ad6fee360e1d8b2770e3a4c3cda98782cf1 100644
--- a/deploy/datasets/testData/bigbrain.js
+++ b/deploy/datasets/testData/bigbrain.js
@@ -75,7 +75,7 @@ module.exports = [
     "parcellationAtlas": [
       {
         "name": "Jülich Cytoarchitechtonic Brain Atlas (human)",
-        "fullId": "https://nexus.humanbrainproject.org/v0/data/minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-25",
+        "fullId": "https://nexus.humanbrainproject.org/v0/data/minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-26",
         "id": [
           "deec923ec31a82f89a9c7c76a6fefd6b",
           "e2d45e028b6da0f6d9fdb9491a4de80a"
diff --git a/deploy/datasets/testData/colin27.js b/deploy/datasets/testData/colin27.js
index 44957a98138564316f0cfd01be4cd58d4e12c4ae..43389fe15164959058e4faace389da3113c51cdd 100644
--- a/deploy/datasets/testData/colin27.js
+++ b/deploy/datasets/testData/colin27.js
@@ -61,7 +61,7 @@ module.exports = [
     "parcellationAtlas": [
       {
         "name": "Jülich Cytoarchitechtonic Brain Atlas (human)",
-        "fullId": "https://nexus.humanbrainproject.org/v0/data/minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-25",
+        "fullId": "https://nexus.humanbrainproject.org/v0/data/minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-26",
         "id": [
           "deec923ec31a82f89a9c7c76a6fefd6b",
           "e2d45e028b6da0f6d9fdb9491a4de80a"
diff --git a/deploy/datasets/util.js b/deploy/datasets/util.js
index 229b03891d1c0b498d734d017655d6a7127ad892..48620a093e4ddc666e29be7f811930354f839e5d 100644
--- a/deploy/datasets/util.js
+++ b/deploy/datasets/util.js
@@ -12,7 +12,7 @@ const KG_IDS = {
     LONG_BUNDLE: 'juelich/iav/atlas/v1.0.0/5',
     SHORT_BUNDLE: 'juelich/iav/atlas/v1.0.0/6',
     JULICH_BRAIN: 'minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579',
-    JULICH_BRAIN_V25: 'minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-25',
+    JULICH_BRAIN_V25: 'minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-26',
     JULICH_BRAIN_V24_BIGBRAIN: 'juelich/iav/atlas/v1.0.0/7'
   }
 }
diff --git a/deploy/lruStore/index.js b/deploy/lruStore/index.js
index e63e2e139f15f5e35904c80d8d3f94cc2f7168da..474f23d5ec9d9ae9462df84439f672954edd66da 100644
--- a/deploy/lruStore/index.js
+++ b/deploy/lruStore/index.js
@@ -20,9 +20,20 @@ const redisProto = REDIS_PROTO || REDIS_RATE_LIMITING_DB_EPHEMERAL_PORT_6379_TCP
 const redisAddr = REDIS_ADDR || REDIS_RATE_LIMITING_DB_EPHEMERAL_PORT_6379_TCP_ADDR || null
 const redisPort = REDIS_PORT || REDIS_RATE_LIMITING_DB_EPHEMERAL_PORT_6379_TCP_PORT || 6379
 
-const userPass = `${REDIS_USERNAME || ''}${( REDIS_PASSWORD && (':' + REDIS_PASSWORD)) || ''}${ (REDIS_USERNAME || REDIS_PASSWORD) && '@'}`
+const userPass = (() => {
+  let returnString = ''
+  if (REDIS_USERNAME) {
+    returnString += REDIS_USERNAME
+  }
+  if (REDIS_PASSWORD) {
+    returnString += `:${REDIS_PASSWORD}`
+  }
+  return returnString === ''
+    ? ''
+    : `${returnString}@`
+})()
 
-const redisURL = redisAddr && `${redisProto}://${userPass}${redisAddr}:${redisPort}`
+const _redisURL = redisAddr && `${redisProto || ''}://${userPass}${redisAddr}:${redisPort}`
 
 const crypto = require('crypto')
 
@@ -45,69 +56,109 @@ const ensureString = val => {
   if (typeof val !== 'string') throw new Error(`both key and val must be string`)
 }
 
-if (redisURL) {
-  const redis = require('redis')
-  const { promisify } = require('util')
-  const client = redis.createClient({
-    url: redisURL
-  })
-  
-  const asyncGet = promisify(client.get).bind(client)
-  const asyncSet = promisify(client.set).bind(client)
-  const asyncDel = promisify(client.del).bind(client)
-
-  const keys = []
-
-  exports.store = {
-    set: async (key, val) => {
-      ensureString(key)
-      ensureString(val)
-      asyncSet(key, val)
-      keys.push(key)
-    },
-    get: async (key) => {
-      ensureString(key)
-      return asyncGet(key)
-    },
-    clear: async auth => {
-      if (auth !== authKey) {
-        getAuthKey()
-        throw new Error(`unauthorized`)
-      }
-      await asyncDel(keys.splice(0))
-    }
+class ExportObj {
+  constructor(){
+    this.StoreType = null
+    this.redisURL = null
+    this.store = null
+    this._rs = null
+    this._rj = null
+
+    this._initPr = new Promise((rs, rj) => {
+      this._rs = rs
+      this._rj = rj
+    })
   }
+}
 
-  exports.StoreType = `redis`
-  console.log(`redis`)
+const exportObj = new ExportObj()
+
+const setupLru = () => {
 
-} else {
   const LRU = require('lru-cache')
-  const store = new LRU({
+  const lruStore = new LRU({
     max: 1024 * 1024 * 1024, // 1gb
     length: (n, key) => n.length,
     maxAge: Infinity, // never expires
   })
 
-  exports.store = {
-    set: async (key, val) => {
+  exportObj.store = {
+    /**
+     * maxage in milli seconds
+     */
+    set: async (key, val, { maxAge } = {}) => {
       ensureString(key)
       ensureString(val)
-      store.set(key, val)
+      lruStore.set(key, val, ...( maxAge ? [ maxAge ] : [] ))
     },
     get: async (key) => {
       ensureString(key)
-      return store.get(key)
+      return lruStore.get(key)
     },
     clear: async auth => {
       if (auth !== authKey) {
         getAuthKey()
         throw new Error(`unauthorized`)
       }
-      store.reset()
+      lruStore.reset()
     }
   }
 
-  exports.StoreType = `lru-cache`
-  console.log(`lru-cache`)
+  exportObj.StoreType = `lru-cache`
+  console.log(`using lru-cache`)
+  exportObj._rs()
+}
+
+if (_redisURL) {
+  const redis = require('redis')
+  const { promisify } = require('util')
+  const client = redis.createClient({
+    url: _redisURL
+  })
+
+  client.on('ready', () => {
+
+    const asyncGet = promisify(client.get).bind(client)
+    const asyncSet = promisify(client.set).bind(client)
+    const asyncDel = promisify(client.del).bind(client)
+  
+    const keys = []
+  
+    /**
+     * maxage in milli seconds
+     */
+     exportObj.store = {
+      set: async (key, val, { maxAge } = {}) => {
+        ensureString(key)
+        ensureString(val)
+        asyncSet(key, val, ...( maxAge ? [ 'PX', maxAge ] : [] ))
+        keys.push(key)
+      },
+      get: async (key) => {
+        ensureString(key)
+        return asyncGet(key)
+      },
+      clear: async auth => {
+        if (auth !== authKey) {
+          getAuthKey()
+          throw new Error(`unauthorized`)
+        }
+        await asyncDel(keys.splice(0))
+      }
+    }
+  
+    exportObj.StoreType = `redis`
+    exportObj.redisURL = _redisURL
+    console.log(`using redis`)
+    exportObj._rs()
+  }).on('error', () => {
+    console.warn(`setting up Redis error, fallback to setupLru`)
+    setupLru()
+    client.quit()
+  })
+
+} else {
+  setupLru()
 }
+
+module.exports = exportObj
\ No newline at end of file
diff --git a/deploy/package.json b/deploy/package.json
index 334fe6153123158a3d503b295af031b934ed49a3..8ac08ced5847f7e9915dabd46bb96e642ad3fcd7 100644
--- a/deploy/package.json
+++ b/deploy/package.json
@@ -13,16 +13,19 @@
   "license": "ISC",
   "dependencies": {
     "archiver": "^3.0.0",
+    "body-parser": "^1.19.0",
+    "connect-redis": "^5.0.0",
     "cookie-parser": "^1.4.5",
     "express": "^4.16.4",
     "express-rate-limit": "^5.1.1",
     "express-session": "^1.15.6",
+    "got": "^10.5.5",
     "hbp-seafile": "^0.1.0",
     "helmet-csp": "^2.8.0",
     "lru-cache": "^5.1.1",
     "memorystore": "^1.6.1",
     "nomiseco": "0.0.2",
-    "openid-client": "^2.4.5",
+    "openid-client": "^4.4.0",
     "passport": "^0.4.0",
     "rate-limit-redis": "^1.7.0",
     "redis": "^3.0.2",
@@ -37,8 +40,6 @@
     "cookie": "^0.4.0",
     "cors": "^2.8.5",
     "dotenv": "^6.2.0",
-    "google-spreadsheet": "^3.0.13",
-    "got": "^10.5.5",
     "mocha": "^6.1.4",
     "nock": "^12.0.3",
     "sinon": "^8.0.2"
diff --git a/deploy/plugins/index.js b/deploy/plugins/index.js
index b9e0e92d50dce693d2e82bc5aff4abc28fd6219b..929f19499786e69b7dc1461a27ae96972e32b8a0 100644
--- a/deploy/plugins/index.js
+++ b/deploy/plugins/index.js
@@ -4,6 +4,8 @@
  */
 
 const express = require('express')
+const lruStore = require('../lruStore')
+const got = require('got')
 const router = express.Router()
 const PLUGIN_URLS = (process.env.PLUGIN_URLS && process.env.PLUGIN_URLS.split(';')) || []
 const STAGING_PLUGIN_URLS = (process.env.STAGING_PLUGIN_URLS && process.env.STAGING_PLUGIN_URLS.split(';')) || []
@@ -15,4 +17,35 @@ router.get('', (_req, res) => {
   ])
 })
 
+const getKey = url => `plugin:manifest-cache:${url}}`
+
+router.get('/manifests', async (_req, res) => {
+
+  const allManifests = await Promise.all([
+    ...PLUGIN_URLS,
+    ...STAGING_PLUGIN_URLS
+  ].map(async url => {
+    const key = getKey(url)
+    
+    await lruStore._initPr
+    const { store } = lruStore
+    
+    try {
+      const storedManifest = await store.get(key)
+      if (storedManifest) return JSON.parse(storedManifest)
+      else throw `not found`
+    } catch (e) {
+      const resp = await got(url)
+      const json = JSON.parse(resp.body)
+      
+      await store.set(key, JSON.stringify(json), { maxAge: 1000 * 60 * 60 })
+      return json
+    }
+  }))
+
+  res.status(200).json(
+    allManifests.filter(v => !!v)
+  )
+})
+
 module.exports = router
\ No newline at end of file
diff --git a/deploy/saneUrl/index.js b/deploy/saneUrl/index.js
index 7aeea8bee754c8e30ae0985c813c18e18f5680ff..b0033f9acefeb56e79bb379cfc0accad09d7b4d6 100644
--- a/deploy/saneUrl/index.js
+++ b/deploy/saneUrl/index.js
@@ -2,47 +2,38 @@ const express = require('express')
 const router = express.Router()
 const RateLimit = require('express-rate-limit')
 const RedisStore = require('rate-limit-redis')
-const { Store, NotFoundError } = require('./store')
+const { FallbackStore: Store, NotFoundError } = require('./store')
+const lruStore = require('../lruStore')
 const { Store: DepcStore } = require('./depcObjStore')
 const { readUserData, saveUserData } = require('../user/store')
 
 const store = new Store()
 const depStore = new DepcStore()
 
-const { 
-  REDIS_PROTO,
-  REDIS_ADDR,
-  REDIS_PORT,
+const { DISABLE_LIMITER, HOSTNAME, HOST_PATHNAME } = process.env
 
-  REDIS_RATE_LIMITING_DB_EPHEMERAL_PORT_6379_TCP_PROTO,
-  REDIS_RATE_LIMITING_DB_EPHEMERAL_PORT_6379_TCP_ADDR,
-  REDIS_RATE_LIMITING_DB_EPHEMERAL_PORT_6379_TCP_PORT,
 
-  REDIS_USERNAME,
-  REDIS_PASSWORD,
-
-  HOSTNAME,
-  HOST_PATHNAME,
-  DISABLE_LIMITER,
-} = process.env
-
-const redisProto = REDIS_PROTO || REDIS_RATE_LIMITING_DB_EPHEMERAL_PORT_6379_TCP_PROTO || 'redis'
-const redisAddr = REDIS_ADDR || REDIS_RATE_LIMITING_DB_EPHEMERAL_PORT_6379_TCP_ADDR || null
-const redisPort = REDIS_PORT || REDIS_RATE_LIMITING_DB_EPHEMERAL_PORT_6379_TCP_PORT || 6379
-
-/**
- * nb this way to set username and pswd can be risky, but given that site adnimistrator sets the username and pswd via env var
- * it should not be a security concern
- */
-const userPass = (REDIS_USERNAME || REDIS_PASSWORD) && `${REDIS_USERNAME || ''}:${REDIS_PASSWORD || ''}@`
-
-const redisURL = redisAddr && `${redisProto}://${userPass || ''}${redisAddr}:${redisPort}`
-
-const limiter = new RateLimit({
-  windowMs: 1e3 * 5,
-  max: 5,
-  ...( redisURL ? { store: new RedisStore({ redisURL }) } : {} )
-})
+let limiter
+const getLimiter = async () => {
+  if (DISABLE_LIMITER) return passthrough
+  
+  if (!!limiter) return limiter
+
+  await lruStore._initPr
+  if (lruStore.redisURL) {
+    limiter = new RateLimit({
+      windowMs: 1e3 * 5,
+      max: 5,
+      store: new RedisStore({ redisURL })
+    })
+  } else {
+    limiter = new RateLimit({
+      windowMs: 1e3 * 5,
+      max: 5,
+    })
+  }
+  return limiter
+}
 
 const passthrough = (_, __, next) => next()
 
@@ -114,7 +105,7 @@ router.get('/:name', async (req, res) => {
 })
 
 router.post('/:name',
-  DISABLE_LIMITER ? passthrough : limiter,
+  getLimiter,
   async (req, res, next) => {
     const { name } = req.params
     try {
diff --git a/deploy/saneUrl/index.spec.js b/deploy/saneUrl/index.spec.js
index 489e405ef3d14b9af21c05e5e020054776bf3be8..3b9a66ec157e6db8e16c75dd8105cadc7fd4b8d9 100644
--- a/deploy/saneUrl/index.spec.js
+++ b/deploy/saneUrl/index.spec.js
@@ -7,6 +7,17 @@ const express = require('express')
 let NotFoundError
 const userStore = require('../user/store')
 
+class StubStore {
+  async set(key, val) {
+
+  }
+  async get(key) {
+
+  }
+}
+
+class StubNotFoundError extends Error{}
+
 const savedUserDataPayload = {
   otherData: 'not relevant data',
   savedCustomLinks: [
@@ -79,7 +90,14 @@ describe('> saneUrl/index.js', () => {
     setStub.resetBehavior()
   })
 
-  describe('> router', () => {
+  before(() => {
+    getStub = sinon.stub(StubStore.prototype, 'get')
+    setStub = sinon.stub(StubStore.prototype, 'set')
+    require.cache[require.resolve('../lruStore')] = {
+      exports: {
+        redisURL: null
+      }
+    }
 
     let server, user
     before(() => {
@@ -212,7 +230,6 @@ describe('> saneUrl/index.js', () => {
           })
         })
         it('> checks if the name is available', async () => {
-          debugger
           await got(`http://localhost:50000/${name}`, {
             method: 'POST',
             headers: {
diff --git a/deploy/saneUrl/store.js b/deploy/saneUrl/store.js
index c6dc3c6af43b8475d1b821e48f7333fead478661..656a070f4fcd5805d816c54390449546fa2885a1 100644
--- a/deploy/saneUrl/store.js
+++ b/deploy/saneUrl/store.js
@@ -17,36 +17,11 @@ const {
   HBP_V2_REFRESH_TOKEN,
   HBP_V2_ACCESS_TOKEN,
 
-  REDIS_PROTO,
-  REDIS_ADDR,
-  REDIS_PORT,
-
-  REDIS_RATE_LIMITING_DB_EPHEMERAL_PORT_6379_TCP_PROTO,
-  REDIS_RATE_LIMITING_DB_EPHEMERAL_PORT_6379_TCP_ADDR,
-  REDIS_RATE_LIMITING_DB_EPHEMERAL_PORT_6379_TCP_PORT,
-
-  REDIS_USERNAME,
-  REDIS_PASSWORD,
-
   DATA_PROXY_URL,
   DATA_PROXY_BUCKETNAME,
 
 } = process.env
 
-const redisProto = REDIS_PROTO || REDIS_RATE_LIMITING_DB_EPHEMERAL_PORT_6379_TCP_PROTO || 'redis'
-const redisAddr = REDIS_ADDR || REDIS_RATE_LIMITING_DB_EPHEMERAL_PORT_6379_TCP_ADDR || null
-const redisPort = REDIS_PORT || REDIS_RATE_LIMITING_DB_EPHEMERAL_PORT_6379_TCP_PORT || 6379
-
-const userPass = (() => {
-  let returnString = ''
-  if (REDIS_USERNAME) returnString += REDIS_USERNAME
-  if (REDIS_PASSWORD) returnString += `:${REDIS_PASSWORD}`
-  if (returnString.length > 0) returnString += `@`
-  return returnString
-})()
-
-const redisURL = redisAddr && `${redisProto}://${userPass}${redisAddr}:${redisPort}`
-
 class NotFoundError extends Error{}
 
 // not part of class, since class is exported, and prototype of class can be easily changed
@@ -78,8 +53,21 @@ function _checkValid(urlString){
   return (new Date() - new Date(expiry)) < 1e3 * 10
 }
 
+class FallbackStore {
+  async get(){
+    throw new Error(`saneurl is currently offline for maintainence.`)
+  }
+  async set(){
+    throw new Error(`saneurl is currently offline for maintainence.`)
+  }
+  async healthCheck() {
+    return false
+  }
+}
+
 class Store {
   constructor(){
+    throw new Error(`Not implemented`)
 
     this.healthFlag = false
     this.seafileHandle = null
@@ -427,6 +415,6 @@ class Store {
   }
 }
 
-
+exports.FallbackStore = FallbackStore
 exports.Store = Store
 exports.NotFoundError = NotFoundError
diff --git a/deploy/saneUrl/store.spec.js b/deploy/saneUrl/store.spec.js
deleted file mode 100644
index c1fe1833e2e1053e8c110cb1bf94bc3747a591af..0000000000000000000000000000000000000000
--- a/deploy/saneUrl/store.spec.js
+++ /dev/null
@@ -1,119 +0,0 @@
-const objStorateRootUrl = `http://fake.obj`
-process.env['OBJ_STORAGE_ROOT_URL'] = objStorateRootUrl
-const DATA_PROXY_URL = process.env['DATA_PROXY_URL'] = 'http://localhost:1234'
-const DATA_PROXY_BUCKETNAME = process.env['DATA_PROXY_BUCKETNAME'] = 'tmp_bucket'
-const tempHost = 'http://localhost:5678'
-const tempPath = '/tmpurl/'
-const tempUrl = `${tempHost}${tempPath}`
-const sinon = require('sinon')
-
-const mockClient = {
-  refresh: sinon.stub()
-}
-const HbpOidcv2 = require('../auth/hbp-oidc-v2')
-const OIDC = require('../auth/oidc')
-
-const { Store } = require('./store')
-const { expect } = require("chai")
-const nock = require('nock')
-
-const objName = `objname`
-const objContent = `objContent`
-
-describe('> store.js', () => {
-  
-  describe('> Store', () => {
-
-    let store,
-      getClientStub,
-      jwtDecodeStub
-
-    before(() => {
-      getClientStub = sinon.stub(HbpOidcv2, 'getClient').returns(Promise.resolve(mockClient))
-      jwtDecodeStub = sinon.stub(OIDC, 'jwtDecode')
-      const nockedProxy = nock(DATA_PROXY_URL)
-      const queryObj = {
-        lifetime: 'very_long'
-      }
-      nockedProxy.get(`/tempurl/${DATA_PROXY_BUCKETNAME}`)
-        .query(queryObj)
-        .reply(200, {
-          url: tempUrl
-        })
-      nockedProxy.put(`/tempurl/${DATA_PROXY_BUCKETNAME}`)
-        .query(queryObj)
-        .reply(200, {
-          url: tempUrl
-        })
-
-      const nockedObjProxy = nock(tempHost)
-      nockedObjProxy
-        .get(`${tempPath}${objName}`)
-        .reply(200, objContent)
-    })
-
-    after(() => {
-      nock.restore()
-      getClientStub.restore()
-      jwtDecodeStub.restore()
-      store.dispose()
-    })
-    afterEach(() => {
-      getClientStub.resetHistory()
-      jwtDecodeStub.resetHistory()
-    })
-
-    describe('> get', () => {
-      let doRefreshTokensStub,
-        initStub,
-        result
-      before(async () => {
-        doRefreshTokensStub = sinon.stub(Store.prototype, 'doRefreshTokens').returns(Promise.resolve())
-        initStub = sinon.stub(Store.prototype, 'init').returns(Promise.resolve())
-        jwtDecodeStub.returns({
-          exp: 1337
-        })
-        store = new Store()
-        result = await store.get(objName)
-      })
-
-      after(() => {
-        doRefreshTokensStub.restore()
-        initStub.restore()
-        store.dispose()
-      })
-
-      it('> returns value is as expected', () => {
-        expect(result).to.equal(objContent)
-      })
-    })
-
-    describe('> set', () => {
-
-      describe('> if no need to refresh', () => {
-
-        before(async () => {
-  
-          store = new Store()
-          await store.set('key', 'value')
-        })
-  
-        after(() => {
-          store.dispose()
-        })
-  
-      })
-
-      describe('> if need to refresh', () => {
-
-        before(async () => {
-          await store.set('key', 'value')
-        })
-  
-        after(() => {
-          store.dispose()
-        })
-      })
-    })
-  })
-})
diff --git a/deploy/templates/query.js b/deploy/templates/query.js
index b7f6f4589e91338cc95af93cd190fa414a9bda2e..9dbbafb4b1c9bbd3d6dc80b97d9128dec7b0532b 100644
--- a/deploy/templates/query.js
+++ b/deploy/templates/query.js
@@ -13,7 +13,8 @@ exports.getAllTemplates = () => new Promise((resolve, reject) => {
     'colin',
     'MNI152',
     'waxholmRatV2_0',
-    'allenMouse'
+    'allenMouse',
+    'freesurfer'
   ]
   resolve(templates)
 })
diff --git a/deploy/user/index.js b/deploy/user/index.js
index 6a6dd14dd2de6eb4c7a5e2cfb1161ed926b07686..dd8a0ab7927cb607ecb8025274bef56306ab8f11 100644
--- a/deploy/user/index.js
+++ b/deploy/user/index.js
@@ -1,10 +1,16 @@
 const express = require('express')
+const bodyParser = require('body-parser')
 const router = express.Router()
 const { readUserData, saveUserData } = require('./store')
 
 const loggedInOnlyMiddleware = (req, res, next) => {
   const { user } = req
-  if (!user) return res.status(401).end()
+  if (!user) {
+    return res.status(200).json({
+      error: true,
+      message: 'Not logged in'
+    })
+  }
   return next()
 }
 
@@ -23,20 +29,58 @@ router.get('/config', loggedInOnlyMiddleware, async (req, res) => {
   }
 })
 
-router.post(
-  '/config',
-  loggedInOnlyMiddleware,
-  express.json(),
-  async (req, res) => {
-    const { user, body } = req
-    try {
-      await saveUserData(user, body)
-      res.status(200).end()
-    } catch (e) {
-      console.error(e)
-      res.status(500).send(e.toString())
+router.get('/pluginPermissions', async (req, res) => {
+  const { user } = req
+  /**
+   * only using session to store user csp for now
+   * in future, if use is logged in, look for **signed** config file, and verify the signature
+   */
+  const permittedCsp = req.session.permittedCsp || {}
+  res.status(200).json(permittedCsp)
+})
+
+router.post('/pluginPermissions', bodyParser.json(), async (req, res) => {
+  const { user, body } = req
+  /**
+   * only using session to store user csp for now
+   * in future, if use is logged in, **signed** config file, and store in user space
+   */
+  
+  const newPermittedCsp = req.session.permittedCsp || {}
+  for (const key in body) {
+    newPermittedCsp[key] = body[key]
+  }
+  req.session.permittedCsp = newPermittedCsp
+  res.status(200).json({ ok: true })
+})
+
+router.delete('/pluginPermissions/:pluginKey', async (req, res) => {
+  const { user, params } = req
+  const { pluginKey } = params
+  /**
+    * only using session to store user csp for now
+    * in future, if use is logged in, **signed** config file, and store in user space
+    */
+  const newPermission = {}
+  const permittedCsp = req.session.permittedCsp || {}
+  for (const key in permittedCsp) {
+    if (pluginKey !== key) {
+      newPermission[key] = permittedCsp[key]
     }
   }
-)
+  req.session.permittedCsp = newPermission
+  res.status(200).json({ ok: true })
+})
+
+router.post('/config', loggedInOnlyMiddleware, bodyParser.json(), async (req, res) => {
+  const { user, body } = req
+  try {
+    await saveUserData(user, body)
+    res.status(200).end()
+  } catch (e) {
+    console.error(e)
+    res.status(500).send(e.toString())
+  }
+})
 
 module.exports = router
\ No newline at end of file
diff --git a/deploy/user/index.spec.js b/deploy/user/index.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..73cb58bbd05692e7a3f770dc55de126dfc673400
--- /dev/null
+++ b/deploy/user/index.spec.js
@@ -0,0 +1,245 @@
+const router = require('./index')
+const app = require('express')()
+const sinon = require('sinon')
+const { stub, spy } = require('sinon')
+const { default: got } = require('got/dist/source')
+const { expect, assert } = require('chai')
+
+const sessionObj = {
+  permittedCspVal: {},
+  get permittedCsp(){
+    return this.permittedCspVal
+  },
+  set permittedCsp(val) {
+
+  }
+}
+
+let userObj = null
+
+const permittedCspSpy = spy(sessionObj, 'permittedCsp', ['get', 'set'])
+
+const middleware = (req, res, next) => {
+  req.session = sessionObj
+  req.user = userObj
+  next()
+}
+
+describe('> user/index.js', () => {
+  let server
+  
+  before(done => {
+    app.use(middleware)
+    app.use(router)
+    server = app.listen(1234)
+    setTimeout(() => {
+      done()
+    }, 1000);
+  })
+
+  afterEach(() => {
+    permittedCspSpy.get.resetHistory()
+    permittedCspSpy.set.resetHistory()
+    sessionObj.permittedCspVal = {}
+    userObj = null
+  })
+
+  after(done => server.close(() => done()))
+
+  describe('> GET ', () => {
+    describe('> user undefined', () => {
+      it('> should return 200, but error in resp', async () => {
+        const { body, statusCode } = await got.get('http://localhost:1234')
+        try {
+          assert(
+            statusCode < 400,
+            'expect the response to be OK'
+          )
+          const { error } = JSON.parse(body)
+          assert(
+            !!error,
+            'expect error is truthy'
+          )
+        } catch (e) {
+          assert(
+            false,
+            `expect no error, but got ${e.toString()}`
+          )
+        }
+      })
+    })
+  
+    describe('> user defined', () => {
+      const user = {
+        foo: 'bar'
+      }
+      beforeEach(() => {
+        userObj = user
+      })
+      it('> should return 200, and user obj in resp', async () => {
+
+        const { body, statusCode } = await got.get('http://localhost:1234')
+        try {
+          assert(
+            statusCode < 400,
+            'expect the response to be ok'
+          )
+          const gotUser = JSON.parse(body)
+          expect(
+            gotUser
+          ).to.deep.equal(user)
+        } catch (e) {
+          assert(
+            false,
+            `expect no error, but got ${e.toString()}`
+          )
+        }
+      })
+    })
+  })
+
+  describe('> GET /pluginPermissions', () => {
+    it('> getter called, setter not called', async () => {
+      await got.get('http://localhost:1234/pluginPermissions')
+
+      assert(
+        permittedCspSpy.get.calledOnce,
+        `permittedCsp getter accessed once`
+      )
+
+      assert(
+        permittedCspSpy.set.notCalled,
+        `permittedCsp setter not called`
+      )
+    })
+    it('> if no value present, returns {}', async () => {
+      sessionObj.permittedCspVal = null
+      const { body } = await got.get('http://localhost:1234/pluginPermissions')
+      expect(JSON.parse(body)).to.deep.equal({})
+    })
+
+    it('> if value present, return value', async () => {
+      const val = {
+        'hot-dog': {
+          'weatherman': 'tolerable'
+        }
+      }
+      sessionObj.permittedCspVal = val
+
+      const { body } = await got.get('http://localhost:1234/pluginPermissions')
+      expect(JSON.parse(body)).to.deep.equal(val)
+    })
+  })
+
+  describe('> POST /pluginPermissions', () => {
+    it('> getter called once, then setter called once', async () => {
+      const jsonPayload = {
+        'hotdog-world': 420
+      }
+      await got.post('http://localhost:1234/pluginPermissions', {
+        json: jsonPayload
+      })
+      assert(
+        permittedCspSpy.get.calledOnce,
+        `permittedCsp getter called once`
+      )
+      assert(
+        permittedCspSpy.set.calledOnce,
+        `permittedCsp setter called once`
+      )
+
+      assert(
+        permittedCspSpy.get.calledBefore(permittedCspSpy.set),
+        `getter called before setter`
+      )
+
+      assert(
+        permittedCspSpy.set.calledWith(jsonPayload),
+        `setter called with payload`
+      )
+    })
+
+    it('> if sessio obj exists, will set with merged obj', async () => {
+      const prevVal = {
+        'foo-bar': [
+          123,
+          'fuzz-buzz'
+        ],
+        'hot-dog-world': 'baz'
+      }
+      sessionObj.permittedCspVal = prevVal
+
+      const jsonPayload = {
+        'hot-dog-world': [
+          'fussball'
+        ]
+      }
+
+      await got.post('http://localhost:1234/pluginPermissions', {
+        json: jsonPayload
+      })
+      assert(
+        permittedCspSpy.set.calledWith({
+          ...prevVal,
+          ...jsonPayload,
+        }),
+        'setter called with merged payload'
+      )
+    })
+  })
+
+  describe('> DELETE /pluginPermissions/:pluginId', () => {
+    const prevVal = {
+      'foo': 'bar',
+      'buzz': 'lightyear'
+    }
+    beforeEach(() => {
+      sessionObj.permittedCspVal = prevVal
+    })
+
+    it('> getter and setter gets called once and in correct order', async () => {
+
+      await got.delete(`http://localhost:1234/pluginPermissions/foolish`)
+
+      assert(
+        permittedCspSpy.get.calledOnce,
+        'getter called once'
+      )
+
+      assert(
+        permittedCspSpy.set.calledOnce,
+        'setter called once'
+      )
+
+      assert(
+        permittedCspSpy.get.calledBefore(permittedCspSpy.set),
+        'getter called before setter'
+      )
+    })
+
+    it('> if attempts at delete non existent key, still returns ok', async () => {
+
+      const { body } = await got.delete(`http://localhost:1234/pluginPermissions/foolish`)
+      const json = JSON.parse(body)
+      expect(json).to.deep.equal({ ok: true })
+
+      assert(
+        permittedCspSpy.set.calledWith(prevVal),
+        'permittedCsp setter called with the prev value (nothing changed)'
+      )
+    })
+
+    it('> if attempts at delete exisiting key, returns ok, and value is set', async () => {
+
+      const { body } = await got.delete(`http://localhost:1234/pluginPermissions/foo`)
+      const json = JSON.parse(body)
+      expect(json).to.deep.equal({ ok: true })
+
+      const { foo, ...rest } = prevVal
+      assert(
+        permittedCspSpy.set.calledWith(rest),
+        'permittedCsp setter called with the prev value, less the deleted key'
+      )
+    })
+  })
+})
\ No newline at end of file
diff --git a/deploy_env.md b/deploy_env.md
new file mode 100644
index 0000000000000000000000000000000000000000..7e4a55a80c10ba180399e31f30a9b71b83bddc5b
--- /dev/null
+++ b/deploy_env.md
@@ -0,0 +1,95 @@
+# Deploy Environment Variables
+
+##### Application
+
+| name | description | default | example |
+| --- | --- | --- | --- |
+| `PORT` | port to listen on | 3000 |
+| `HOST_PATHNAME` | pathname to listen on, restrictions: leading slash, no trailing slash | `''` | `/viewer` |
+| `SESSIONSECRET` | session secret for cookie session |
+| `NODE_ENV` | determines where the built viewer will be served from | | `production` |
+| `PRECOMPUTED_SERVER` | redirect data uri to another server. Useful for offline demos | | `http://localhost:8080/precomputed/` |
+| `LOCAL_CDN` | rewrite cdns to local server. useful for offlnie demo | | `http://localhost:7080/` |
+| `PLUGIN_URLS` | semi colon separated urls to be returned when user queries plugins | `''`
+| `STAGING_PLUGIN_URLS` | semi colon separated urls to be returned when user queries plugins | `''`
+| `USE_LOGO` | possible values are `hbp`, `ebrains`, `fzj` | `hbp` | `ebrains` |
+| `__DEBUG__` | debug flag | 
+
+##### ebrains user authentication
+
+| name | description | default | example |
+| --- | --- | --- | --- |
+| `HOSTNAME` | 
+| `HBP_CLIENTID` | `{HOSTNAME}{HOST_PATHNAME}/hbp-oidc/cb` |
+| `HBP_CLIENTSECRET` |
+| `HBP_CLIENTID_V2` | `{HOSTNAME}{HOST_PATHNAME}/hbp-oidc-v2/cb`
+| `HBP_CLIENTSECRET_V2` | 
+
+##### Querying ebrains knowledge graph
+
+| name | description | default | example |
+| --- | --- | --- | --- |
+| `REFRESH_TOKEN` |
+| `ACCESS_TOKEN` | **nb** as access tokens are usually short lived, this should only be set for development purposes 
+| `KG_ROOT` | | `https://kg.humanbrainproject.eu/query` |
+| `KG_SEARCH_VOCAB` | | `https://schema.hbp.eu/myQuery/` |
+| `KG_DATASET_SEARCH_QUERY_NAME` | | `interactiveViewerKgQuery-v0_3` |
+| `KG_DATASET_SEARCH_PATH` | | `/minds/core/dataset/v1.0.0` |
+| `KG_SEARCH_SIZE` | | `1000` |
+| `KG_SPATIAL_DATASET_SEARCH_QUERY_NAME` | | `iav-spatial-query-v2` |
+| `KG_SPATIAL_DATASET_SEARCH_PATH` | | `/neuroglancer/seeg/coordinate/v1.0.0` | 
+
+##### Logging
+
+| name | description | default | example |
+| --- | --- | --- | --- |
+| `FLUENT_PROTOCOL` | protocol for fluent logging | `http` |
+| `FLUENT_HOST` | host for fluent logging | `localhost` |
+| `FLUENT_PORT` | port for fluent logging | 24224 |
+| `IAV_NAME` | application name to be logged | `IAV` | 
+| `IAV_STAGE` | deploy of the application | `unnamed-stage` |
+
+##### CSP
+
+| name | description | default | example |
+| --- | --- | --- | --- |
+| `DISABLE_CSP` | disable csp | | `true` |
+| `CSP_REPORT_URI` | report uri for csp violations | `/report-violation` |
+| `NODE_ENV` | set to `production` to disable [`reportOnly`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only) | `null` |
+| `SCRIPT_SRC` | `JSON.stringify`'ed array of allowed scriptSrc | `[]` |
+| `CSP_CONNECT_SRC` | `JSON.stringify`'ed array of allowed dataSrc | `[]` |
+| `WHITE_LIST_SRC` | `JSON.stringify`'ed array of allowed src | `[]` |
+| `PROXY_HOSTNAME_WHITELIST` |
+
+##### Rate limiting
+
+| name | description | default | example |
+| --- | --- | --- | --- |
+| `REDIS_PROTO` | fall back to `REDIS_RATE_LIMITING_DB_EPHEMERAL_PORT_6379_TCP_PROTO` |
+| `REDIS_ADDR` | fall back to `REDIS_RATE_LIMITING_DB_EPHEMERAL_PORT_6379_TCP_ADDR` |
+| `REDIS_PORT` | fall back to `REDIS_RATE_LIMITING_DB_EPHEMERAL_PORT_6379_TCP_PORT` |
+| `REDIS_USERNAME` |
+| `REDIS_PASSWORD` |
+| `DISABLE_LIMITER` | disable rate limiting (maybe required for automated tests) |
+
+##### SaneUrl
+
+| name | description | default | example |
+| --- | --- | --- | --- |
+| `OBJ_STORAGE_AUTH_URL` |
+| `OBJ_STORAGE_IDP_NAME` |
+| `OBJ_STORAGE_IDP_PROTO` |
+| `OBJ_STORAGE_IDP_URL` |
+| `OBJ_STORAGE_USERNAME` |
+| `OBJ_STORAGE_PASSWORD` |
+| `OBJ_STORAGE_PROJECT_ID` |
+| `OBJ_STORAGE_ROOT_URL` |
+
+##### Test deploy denvironments
+
+| name | description | default | example |
+| --- | --- | --- | --- |
+| `SERVICE_ACCOUNT_CRED` | 
+| `SERVICE_ACCOUNT_CRED_PATH` | 
+| `WAXHOLM_RAT_GOOGLE_SHEET_ID` |
+| `SKIP_RETRY_TEST` | retry tests contains some timeouts, which may slow down tests | 
diff --git a/docs/releases/v2.4.0.md b/docs/releases/v2.4.0.md
new file mode 100644
index 0000000000000000000000000000000000000000..3a2e26be6b9a9766e88829860ddcf88473c10eb5
--- /dev/null
+++ b/docs/releases/v2.4.0.md
@@ -0,0 +1,28 @@
+# v2.4.0
+
+## Bugfixes
+
+- fixes UI issue, where chips wraps on smaller screen (#740)
+- regions with the same labelIndex without colour will no longer have the same colour (#750)
+- fixes gpuLimit not applying properly (#765)
+- fixes minor issue when switching back to multi version parc (#831)
+- fixes chips wrapping and scrolling on smaller devices (#720)
+
+## New features
+
+- plugins will now follow a permission model, if they would like to access external resources
+- only top level mesh(es) will be loaded (#890). This should drastically improve atlases with hierarchical surface meshes (e.g. Allen CCF v3).
+- Preliminary support for displaying of SWC (#907)
+- Preliminary support for freesurfer (#900)
+- Allow for finer controls over meshes display (#883, #470)
+- Added `quick tour` feature (#899)
+- Added user annotation feature (#886, #888, #887)
+
+## Under the hood stuff
+
+- refactored code, added additional test coverage
+- reworked screenshot component, removed html2canvas dependency
+- deprecated `getToastHandler` and `geModalHandler` in plugin API
+- updated `openid-client` (#696) and removed unused dependencies
+- allowing for `/user` and `/user/config` endpoints to return non error, even if the user is not logged in. (#767)
+- use routing instead of query param (#858)
diff --git a/e2e/chromeOpts.js b/e2e/chromeOpts.js
index 7e4076611c1484a12ce26fd9b26f5e2246d1920d..fd9b30a00b9eca3251366ae8667391e5ebb1a6b3 100644
--- a/e2e/chromeOpts.js
+++ b/e2e/chromeOpts.js
@@ -6,6 +6,7 @@ module.exports = [
   ...(process.env.ENABLE_GPU ? []: ['--disable-gpu']),
   '--disable-setuid-sandbox',
   "--disable-extensions",
+  '--disable-dev-shm-usage',
   `--window-size=${width},${height}`,
   '--disable-application-cache'
 ]
\ No newline at end of file
diff --git a/e2e/protractor.conf.js b/e2e/protractor.conf.js
index 5b97da11f2556d369ae5f7c7e1a807a63b19fd38..2fbc49e5b62647cd050e47ea1fa2dc513c80753d 100644
--- a/e2e/protractor.conf.js
+++ b/e2e/protractor.conf.js
@@ -2,6 +2,12 @@
 // n.b. to start selenium, run npm run wd -- update && npm run wd -- start
 // n.b. you will need to run `npm i --no-save puppeteer`, so that normal download script does not download chrome binary
 const chromeOpts = require('./chromeOpts')
+const fs = require('fs')
+const path = require('path')
+const { promisify } = require('util')
+const asyncWrite = promisify(fs.writeFile)
+const asyncMkdir = promisify(fs.mkdir)
+
 const SELENIUM_ADDRESS = process.env.SELENIUM_ADDRESS
 
 const {
@@ -16,7 +22,7 @@ const PROTRACTOR_SPECS = process.env.PROTRACTOR_SPECS
 const localConfig = {
   ...(SELENIUM_ADDRESS
     ? { seleniumAddress: SELENIUM_ADDRESS }
-    : { directConnect: true } 
+    : { directConnect: true }
   ),
   capabilities: {
     // Use headless chrome
@@ -31,6 +37,30 @@ const localConfig = {
           : { binary: (() => require('puppeteer').executablePath())() }
       )
     }
+  },
+  onPrepare: function() {
+    // polyfill for node10 or lower
+    if (typeof globalThis === 'undefined') global.globalThis = {}
+    jasmine.getEnv().addReporter({
+      specDone: async ({ status, id, message, fullName, ...rest }) => {
+        if (status === 'failed') {
+          console.log(`spec failed, taking screenshot`)
+          const b64 = await globalThis.IAVBase.takeScreenshot()
+          const dir = './scrnsht/'
+          await asyncMkdir(dir, { recursive: true })
+          await asyncWrite(
+            path.join(dir, `${id}.png`),
+            b64,
+            'base64'
+          )
+          await asyncWrite(
+            path.join(dir, `${id}.txt`),
+            JSON.stringify({ id, status, message, fullName }, null, 2),
+            'utf-8'
+          )
+        }
+      }
+    })
   }
 }
 
@@ -41,13 +71,13 @@ let bsLocal
 /**
  * config adapted from
  * https://github.com/browserstack/protractor-browserstack
- * 
+ *
  * MIT licensed
  */
 const bsConfig = {
   'browserstackUser': BROWSERSTACK_USERNAME,
   'browserstackKey': BROWSERSTACK_ACCESS_KEY,
-  
+
   'capabilities': {
     'build': 'protractor-browserstack',
     'name': BROWSERSTACK_TEST_NAME || 'iav_e2e',
@@ -91,7 +121,7 @@ exports.config = {
   jasmineNodeOpts: {
     defaultTimeoutInterval: 1000 * 60 * 10
   },
-  
+
   ...(
     BROWSERSTACK_ACCESS_KEY && BROWSERSTACK_USERNAME
     ? bsConfig
@@ -101,4 +131,4 @@ exports.config = {
   ...(
     (directConnect && { directConnect }) || {}
   )
-}
\ No newline at end of file
+}
diff --git a/e2e/src/advanced/favDatasets.prod.e2e-spec.js b/e2e/src/advanced/favDatasets.prod.e2e-spec.js
index 9cb93907a73c446fbbf63d40a7968b2e1248dfa3..ec5de6d63b1b5a84f080def6072457c218f0235c 100644
--- a/e2e/src/advanced/favDatasets.prod.e2e-spec.js
+++ b/e2e/src/advanced/favDatasets.prod.e2e-spec.js
@@ -12,7 +12,7 @@ const templates = [
 const area = 'Area hOc1 (V1, 17, CalcS)'
 
 const receptorName = `Density measurements of different receptors for Area hOc1 (V1, 17, CalcS) [human, v1.0]`
-const pmapName = `Probabilistic cytoarchitectonic map of Area hOc1 (V1, 17, CalcS) (v2.4)`
+const ieegDatasetName = `Human Intracranial EEG Database (HID) [cohort I]`
 
 // TODO finish writing tests
 describe(`fav'ing dataset`, () => {
@@ -56,7 +56,7 @@ describe(`fav'ing dataset`, () => {
     const datasets = await iavPage.getVisibleDatasets()
 
     const receptorIndex = datasets.indexOf(receptorName)
-    const probMap = datasets.indexOf(pmapName)
+    const probMap = datasets.indexOf(ieegDatasetName)
     expect(receptorIndex).toBeGreaterThanOrEqual(0)
     expect(probMap).toBeGreaterThanOrEqual(0)
 
@@ -68,7 +68,7 @@ describe(`fav'ing dataset`, () => {
     await iavPage.togglePinNthDataset(probMap)
     await iavPage.wait(500)
     const txt2 = await iavPage.getSnackbarMessage()
-    expect(txt2).toEqual(`Pinned dataset: ${pmapName}`)
+    expect(txt2).toEqual(`Pinned dataset: ${ieegDatasetName}`)
   })
 
   describe('> fav dataset list', () => {
@@ -77,7 +77,7 @@ describe(`fav'ing dataset`, () => {
       const datasets = await iavPage.getVisibleDatasets()
 
       const receptorIndex = datasets.indexOf(receptorName)
-      const probMap = datasets.indexOf(pmapName)
+      const probMap = datasets.indexOf(ieegDatasetName)
 
       await iavPage.togglePinNthDataset(receptorIndex)
       await iavPage.togglePinNthDataset(probMap)
@@ -190,7 +190,7 @@ describe(`fav'ing dataset`, () => {
       const datasets = await iavPage.getVisibleDatasets()
 
       const receptorIndex = datasets.indexOf(receptorName)
-      const probMap = datasets.indexOf(pmapName)
+      const probMap = datasets.indexOf(ieegDatasetName)
 
       await iavPage.togglePinNthDataset(receptorIndex)
       await iavPage.togglePinNthDataset(probMap)
@@ -204,7 +204,7 @@ describe(`fav'ing dataset`, () => {
       
       expect(textsArr.length).toEqual(2)
       expect(textsArr).toContain(receptorName)
-      expect(textsArr).toContain(pmapName)
+      expect(textsArr).toContain(ieegDatasetName)
     })
 
     it('> dataset detail can be launched via collection', async () => {
diff --git a/e2e/src/advanced/history.prod.e2e-spec.js b/e2e/src/advanced/history.prod.e2e-spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..c8cf1a937391989f4ec8628268e91478e4d91bde
--- /dev/null
+++ b/e2e/src/advanced/history.prod.e2e-spec.js
@@ -0,0 +1,60 @@
+const { WdIavPage } = require('../../util/selenium/iav')
+const { AtlasPage } = require('../util')
+const { height, width } = require("../../opts")
+const atlasName = `Multilevel Human Atlas`
+
+describe('> navigating IAV via history', () => {
+  let iavPage = new AtlasPage()
+  beforeEach(async () => {
+    iavPage = new AtlasPage()
+    await iavPage.init()
+    await iavPage.goto()
+    await iavPage.setAtlasSpecifications(atlasName)
+    
+    await iavPage.wait(5000)
+    await iavPage.waitUntilAllChunksLoaded()
+  })
+
+  it('> after navigate, navigation state differs', async () => {
+    const nav = await iavPage.getNavigationState()
+    await iavPage.cursorMoveToAndDrag({
+      position: [ Math.round(width / 4), Math.round(height / 4) ],
+      delta: [ Math.round(width / 8), Math.round(height / 8) ]
+    })
+    const nav2 = await iavPage.getNavigationState()
+
+    // expect some positions to change after dragging
+    expect(
+      nav.position.some((v, idx) => v !== nav2.position[idx])
+    ).toBeTrue()
+  })
+
+  it('> after navigate, history back, navigation should ', async () => {
+    const nav = await iavPage.getNavigationState()
+    await iavPage.cursorMoveToAndDrag({
+      position: [ Math.round(width / 4), Math.round(height / 4) ],
+      delta: [ Math.round(width / 8), Math.round(height / 8) ]
+    })
+    await iavPage.wait(5000)
+    await iavPage.historyBack()
+    await iavPage.wait(5000)
+
+    const nav2 = await iavPage.getNavigationState()
+    // expect some positions to change after dragging
+    expect(
+      nav.position.every((v, idx) => v === nav2.position[idx])
+    ).toBeTrue()
+  })
+
+  it('> history back, viewer should despawn ', async () => {
+
+    await iavPage.wait(5000)
+    await iavPage.historyBack()
+    await iavPage.wait(5000)
+
+    const flag = await iavPage.viewerIsPopulated()
+    expect(flag).toBeFalse()
+
+  })
+
+})
\ No newline at end of file
diff --git a/e2e/src/advanced/pluginApi.prod.e2e-spec.js b/e2e/src/advanced/pluginApi.prod.e2e-spec.js
index a5c14f975d1730239ce243bf972670ee3c90c515..3abcab7e33d34fbd737699e18360a4f750af662b 100644
--- a/e2e/src/advanced/pluginApi.prod.e2e-spec.js
+++ b/e2e/src/advanced/pluginApi.prod.e2e-spec.js
@@ -1,5 +1,6 @@
 const { AtlasPage } = require('../util')
-const template = 'ICBM 2009c Nonlinear Asymmetric'
+const atlasName = 'Multilevel Human Atlas'
+const template = 'ICBM 152 2009c Nonlinear Asymmetric'
 
 const pluginName = `fzj.xg.testWidget`
 const pluginDisplayName = `Test Widget Title`
@@ -24,7 +25,7 @@ describe('> plugin api', () => {
     iavPage = new AtlasPage()
     await iavPage.init()
     await iavPage.goto()
-    await iavPage.selectTitleCard(template)
+    await iavPage.setAtlasSpecifications(atlasName)
     await iavPage.wait(500)
     await iavPage.waitUntilAllChunksLoaded()
   })
@@ -76,6 +77,8 @@ describe('> plugin api', () => {
 
       describe('> getUserToSelectARegion', () => {
         let originalTitle
+        const cursorPos = [250, 660]
+        const cursorRegion = 'Area hOc1 (V1, 17, CalcS)'
         beforeEach(async () => {
           originalTitle = await iavPage.execScript(() => window.document.title)
 
@@ -114,10 +117,10 @@ describe('> plugin api', () => {
         })
 
         it('> on clicking region, resolves pr', async () => {
-          await iavPage.cursorMoveToAndClick({ position: [600, 490] })
+          await iavPage.cursorMoveToAndClick({ position: cursorPos })
           await iavPage.wait(500)
           const newTitle = await iavPage.execScript(() => window.document.title)
-          expect(newTitle).toEqual(`success Area 6ma (preSMA, mesial SFG) - left hemisphere`)
+          expect(newTitle).toEqual(`success ${cursorRegion}`)
         })
 
         it('> on failusre, clears modal', async () => {
@@ -134,7 +137,7 @@ describe('> plugin api', () => {
 
         it('> on success, clears modal', async () => {
 
-          await iavPage.cursorMoveToAndClick({ position: [600, 490] })
+          await iavPage.cursorMoveToAndClick({ position: cursorPos })
           await iavPage.wait(500)
           
           try {
diff --git a/e2e/src/advanced/urlParsing.prod.e2e-spec.js b/e2e/src/advanced/urlParsing.prod.e2e-spec.js
index 4a95357e7bded5b82a6ab5a275adba8875382b7d..c761e47e3e8c84dcbb34a03bf5f01916490359a7 100644
--- a/e2e/src/advanced/urlParsing.prod.e2e-spec.js
+++ b/e2e/src/advanced/urlParsing.prod.e2e-spec.js
@@ -1,6 +1,4 @@
 const { AtlasPage } = require("../util")
-const proxy = require('selenium-webdriver/proxy')
-const { ARIA_LABELS } = require('../../../common/constants')
 
 describe('> url parsing', () => {
   let iavPage
@@ -8,20 +6,16 @@ describe('> url parsing', () => {
   beforeEach(async () => {
     iavPage = new AtlasPage()
     await iavPage.init()
-    
   })
 
-  // tracking issue: https://github.com/HumanBrainProject/interactive-viewer/issues/455
-  // reenable when fixed
-  // it('incorrectly defined templateSelected should clear searchparam', async () => {
-  //   const search = '/?templateSelected=NoName2&parcellationSelected=NoName'
-  //   const page = await browser.newPage()
-  //   await page.goto(`${ATLAS_URL}${search}`, {waitUntil: 'networkidle2'})
-  //   await page.waitFor(500)
-  //   const searchParam = await getSearchParam(page)
-  //   const searchParamObj = new URLSearchParams(searchParam)
-  //   expect(searchParamObj.get('templateSelected')).toBeNull()
-  // })
+  it('incorrectly defined templateSelected should clear searchparam', async () => {
+    const searchParam = '/?templateSelected=NoName2&parcellationSelected=NoName'
+    
+    await iavPage.goto(searchParam, { doNotAutomate: true })
+    await iavPage.waitFor(500)
+    const text = await iavPage.getSnackbarMessage()
+    expect(text).toEqual(`Template not found.`)
+  })
 
   it('> navigation state should be perserved', async () => {
 
@@ -81,16 +75,23 @@ describe('> url parsing', () => {
     await iavPage.wait(5000)
     await iavPage.waitForAsync()
     const log = await iavPage.getLog()
-    const filteredLog = log.filter(({ message }) => !/Access-Control-Allow-Origin/.test(message))
+    const filteredLog = log
+      // in headless & non gpu mode, a lot of webgl warnings are thrown
+      .filter(({ level }) => level.toString() === 'SEVERE')
+      .filter(({ message }) => !/Access-Control-Allow-Origin/.test(message))
 
     // expecting some errors in the console. In catastrophic event, there will most likely be looped errors (on each render cycle)
+    // capture logs and write to spec https://stackoverflow.com/a/24980483/6059235
     expect(
       filteredLog.length
-    ).toBeLessThan(50)
+    ).toBeLessThan(
+      50,
+      JSON.stringify(filteredLog)
+    )
   })
 
   it('> if niftiLayers are defined, parcellation layer should be hidden', async () => {
-    const url = `/?parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&templateSelected=MNI+Colin+27&navigation=0_0_0_1__-0.2753947079181671_0.6631333827972412_-0.6360703706741333_0.2825356423854828__3000000__-17800000_-6700000_-7500000__200000&regionsSelected=142&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FJuBrain%2Fv2.2c%2FPMaps%2FBforebrain_4.nii`
+    const url = `/?parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&templateSelected=MNI+Colin+27&cNavigation=0.0.0.-W000.._NjRq.2-Klk_._-Hmu.2_BdKx..DMVW..1vjMG.4eIG8~.hqT5~..10vB&regionsSelected=142&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.org%2Fprecomputed%2FJuBrain%2Fv2.2c%2FPMaps%2FBforebrain_4.nii`
     await iavPage.goto(url)
     await iavPage.clearAlerts()
 
@@ -101,28 +102,12 @@ describe('> url parsing', () => {
     expect(red).toEqual(blue)
   })
 
-  it('> if niftiLayers is defined, after parsing, it should persist', async () => {
-    const url = `/?templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&niftiLayers=https%3A%2F%2Fneuroglancer.humanbrainproject.eu%2Fprecomputed%2FJuBrain%2F17%2Ficbm152casym%2Fpmaps%2FVisual_hOc1_r_N10_nlin2MNI152ASYM2009C_2.4_publicP_a48ca5d938781ebaf1eaa25f59df74d0.nii.gz`
-    await iavPage.goto(url)
-    await iavPage.clearAlerts()
-
-    // TODO use execScript from iavPage API
-    const niftiLayers = await iavPage._driver.executeScript(() => {
-      const search = new URLSearchParams(window.location.search)
-      return search.get('niftiLayers')
-    })
-    const expected = `https://neuroglancer.humanbrainproject.eu/precomputed/JuBrain/17/icbm152casym/pmaps/Visual_hOc1_r_N10_nlin2MNI152ASYM2009C_2.4_publicP_a48ca5d938781ebaf1eaa25f59df74d0.nii.gz`
-    expect(niftiLayers).toEqual(expected)
-  })
-  
+  it('> if using niftilayers should show deprecation worning')
 
-  it('> pluginStates should result in xhr to get pluginManifest', async () => {
+  it('> pluginStates should be fetched even if no template or parc are selected', async () => {
 
     const searchParam = new URLSearchParams()
-    searchParam.set('templateSelected', 'MNI 152 ICBM 2009c Nonlinear Asymmetric')
-    searchParam.set('parcellationSelected', 'JuBrain Cytoarchitectonic Atlas')
     searchParam.set('pluginStates', 'http://localhost:3001/manifest.json')
-
     await iavPage.goto(`/?${searchParam.toString()}`, { interceptHttp: true, doNotAutomate: true })
     await iavPage.wait(10000)
     const interceptedCalls = await iavPage.getInterceptedHttpCalls()
@@ -136,33 +121,24 @@ describe('> url parsing', () => {
     ))
   })
 
-  it('> if datasetPreview is set, should load with previews', async () => {
-    const url = `http://localhost:3000/?templateSelected=MNI+152+ICBM+2009c+Nonlinear+Asymmetric&parcellationSelected=JuBrain+Cytoarchitectonic+Atlas&previewingDatasetFiles=%5B%7B%22datasetId%22%3A%22e715e1f7-2079-45c4-a67f-f76b102acfce%22%2C%22filename%22%3A%22fingerprint%22%7D%2C%7B%22datasetId%22%3A%22e715e1f7-2079-45c4-a67f-f76b102acfce%22%2C%22filename%22%3A%22GABA%E1%B4%80%28BZ%29%2Fautoradiography%22%7D%2C%7B%22datasetId%22%3A%22e715e1f7-2079-45c4-a67f-f76b102acfce%22%2C%22filename%22%3A%22GABA%E1%B4%80%28BZ%29%2Fprofile%22%7D%5D`
-    const datasetPreview = [
-      {
-        "datasetId": "e715e1f7-2079-45c4-a67f-f76b102acfce",
-        "filename": "fingerprint"
-      },
-      {
-        "datasetId": "e715e1f7-2079-45c4-a67f-f76b102acfce",
-        "filename": "GABAá´€(BZ)/autoradiography"
-      },
-      {
-        "datasetId": "e715e1f7-2079-45c4-a67f-f76b102acfce",
-        "filename": "GABAá´€(BZ)/profile"
-      }
-    ]
+  it('> pluginStates should result in xhr to get pluginManifest', async () => {
 
     const searchParam = new URLSearchParams()
-
     searchParam.set('templateSelected', 'MNI 152 ICBM 2009c Nonlinear Asymmetric')
     searchParam.set('parcellationSelected', 'JuBrain Cytoarchitectonic Atlas')
-    searchParam.set('previewingDatasetFiles', JSON.stringify(datasetPreview))
-    await iavPage.goto(`/?${searchParam.toString()}`)
+    searchParam.set('pluginStates', 'http://localhost:3001/manifest.json')
 
-    const visibleArr = await iavPage.areVisible(`[aria-label="${ARIA_LABELS.DATASET_FILE_PREVIEW}"]`)
-    expect(visibleArr.length).toEqual(3)
-    expect(visibleArr).toEqual([true, true, true])
+    await iavPage.goto(`/?${searchParam.toString()}`, { interceptHttp: true, doNotAutomate: true })
+    await iavPage.wait(10000)
+    const interceptedCalls = await iavPage.getInterceptedHttpCalls()
+    expect(
+      interceptedCalls
+    ).toContain(jasmine.objectContaining(
+      {
+        method: 'GET',
+        url: 'http://localhost:3001/manifest.json'
+      }
+    ))
   })
 
 })
diff --git a/e2e/src/navigating/btnZoomInOut.prod.e2e-spec.js b/e2e/src/navigating/btnZoomInOut.prod.e2e-spec.js
index e54ee6f0d684ee588297745de1c36296f172aec6..d8349964da698991f33631ca33365f6b4f0fbacb 100644
--- a/e2e/src/navigating/btnZoomInOut.prod.e2e-spec.js
+++ b/e2e/src/navigating/btnZoomInOut.prod.e2e-spec.js
@@ -15,16 +15,13 @@ describe('> zoom in btns on panels', () => {
     })
     it('> panel btns do not show', async () => {
       
-      await iavPage.cursorMoveTo({
-        position: [width / 4, height/4]
-      })
+      await iavPage.cursorMoveToElement(`[aria-label="${ARIA_LABELS.SELECT_ATLAS}"]`)
 
       await iavPage.wait(500)
 
       const visibleFlags = await iavPage.areVisible(`[aria-label="${ARIA_LABELS.ZOOM_IN}"]`)
       expect(visibleFlags.length).toBe(4)
       expect(visibleFlags).toEqual([false, false, false, false])
-      await iavPage.clickSideNavTab()
     })
   })
 
diff --git a/e2e/src/navigating/originDataset.prod.e2e-spec.js b/e2e/src/navigating/originDataset.prod.e2e-spec.js
index ff4aea1de27ca8338f393a8c9e0f1e560272d2ed..db6c60dd2e698ed814424910419918e778e30412 100644
--- a/e2e/src/navigating/originDataset.prod.e2e-spec.js
+++ b/e2e/src/navigating/originDataset.prod.e2e-spec.js
@@ -1,6 +1,6 @@
 const { AtlasPage } = require("../util")
 const { ARIA_LABELS } = require('../../../common/constants')
-const { SHOW_CONNECTIVITY_DATA, SHOW_IN_OTHER_REF_SPACE, SHOW_ORIGIN_DATASET } = ARIA_LABELS
+const { SHOW_ORIGIN_DATASET } = ARIA_LABELS
 
 const cssSelector = `[aria-label="${SHOW_ORIGIN_DATASET}"]`
 
diff --git a/e2e/src/selecting/atlas.prod.e2e-spec.js b/e2e/src/selecting/atlas.prod.e2e-spec.js
index e5b703809e8a39e397958a6ded5d0347366b3c97..370fb3e17de25d3c70a4003776184178963a559c 100644
--- a/e2e/src/selecting/atlas.prod.e2e-spec.js
+++ b/e2e/src/selecting/atlas.prod.e2e-spec.js
@@ -19,75 +19,75 @@ describe('> atlases are generally available', () => {
   })
 })
 
-describe('> generic atlas behaviours', () => {
-  let atlasPage = new AtlasPage()
-  beforeEach(async () => {
-    atlasPage = new AtlasPage()
-    await atlasPage.init()
-  })
-  for (const atlas of atlases) {
-    describe(`> of ${atlas}`, () => {
-      it('> on launch, shows atlas name as a pill', async () => {
-        await atlasPage.goto()
-        await atlasPage.clearAlerts()
-        await atlasPage.setAtlasSpecifications(atlas)
-        await atlasPage.wait(500)
-        await atlasPage.waitUntilAllChunksLoaded()
-        const txtArr = await atlasPage.getAllChipsText()
-        expect(
-          txtArr.filter(v => v.replace(/\s/g, '').length > 0).length
-        ).toBeGreaterThan(0)
-        console.log(`chip shows: ${txtArr.join(', ')}`)
-      })
-    })
-  }
-})
+// describe('> generic atlas behaviours', () => {
+//   let atlasPage = new AtlasPage()
+//   beforeEach(async () => {
+//     atlasPage = new AtlasPage()
+//     await atlasPage.init()
+//   })
+//   for (const atlas of atlases) {
+//     describe(`> of ${atlas}`, () => {
+//       it('> on launch, shows atlas name as a pill', async () => {
+//         await atlasPage.goto()
+//         await atlasPage.clearAlerts()
+//         await atlasPage.setAtlasSpecifications(atlas)
+//         await atlasPage.wait(500)
+//         await atlasPage.waitUntilAllChunksLoaded()
+//         const txtArr = await atlasPage.getAllChipsText()
+//         expect(
+//           txtArr.filter(v => v.replace(/\s/g, '').length > 0).length
+//         ).toBeGreaterThan(0)
+//         console.log(`chip shows: ${txtArr.join(', ')}`)
+//       })
+//     })
+//   }
+// })
 
-describe('> in human multi level', () => {
-  let atlasPage = new AtlasPage()
-  beforeAll(async () => {
-    atlasPage = new AtlasPage()
-    await atlasPage.init()
-    await atlasPage.goto()
-    await atlasPage.clearAlerts()
-  })
-  describe('> removal of additional layers should restore base layer', () => {
-    it('> in mni152', async () => {
-      await atlasPage.setAtlasSpecifications(atlases[1], [`Fibre tracts`, `Short Bundle`])
-      await atlasPage.wait(500)
-      await atlasPage.waitForAsync()
+// describe('> in human multi level', () => {
+//   let atlasPage = new AtlasPage()
+//   beforeAll(async () => {
+//     atlasPage = new AtlasPage()
+//     await atlasPage.init()
+//     await atlasPage.goto()
+//     await atlasPage.clearAlerts()
+//   })
+//   describe('> removal of additional layers should restore base layer', () => {
+//     it('> in mni152', async () => {
+//       await atlasPage.setAtlasSpecifications(atlases[1], [`Fibre tracts`, `Short Bundle`])
+//       await atlasPage.wait(500)
+//       await atlasPage.waitForAsync()
 
-      const txtArr = await atlasPage.getAllChipsText()
-      expect(
-        txtArr.find(txt => /short bundle/i.test(txt))
-      ).toBeTruthy()
+//       const txtArr = await atlasPage.getAllChipsText()
+//       expect(
+//         txtArr.find(txt => /short bundle/i.test(txt))
+//       ).toBeTruthy()
 
-      await atlasPage.clickChip(/short bundle/i, '.fa-times')
+//       await atlasPage.clickChip(/short bundle/i, '.fa-times')
 
-      const txtArr2 = await atlasPage.getAllChipsText()
-      expect(
-        txtArr2.find(txt => /short bundle/i.test(txt))
-      ).toBeFalsy()
+//       const txtArr2 = await atlasPage.getAllChipsText()
+//       expect(
+//         txtArr2.find(txt => /short bundle/i.test(txt))
+//       ).toBeFalsy()
       
-    })
+//     })
 
-    it('> in bigbrain', async () => {
-      await atlasPage.setAtlasSpecifications(atlases[1], [/isocortex/i])
-      await atlasPage.wait(500)
-      await atlasPage.waitForAsync()
+//     it('> in bigbrain', async () => {
+//       await atlasPage.setAtlasSpecifications(atlases[1], [/isocortex/i])
+//       await atlasPage.wait(500)
+//       await atlasPage.waitForAsync()
 
-      const txtArr = await atlasPage.getAllChipsText()
-      expect(
-        txtArr.find(txt => /isocortex/i.test(txt))
-      ).toBeTruthy()
+//       const txtArr = await atlasPage.getAllChipsText()
+//       expect(
+//         txtArr.find(txt => /isocortex/i.test(txt))
+//       ).toBeTruthy()
 
-      await atlasPage.clickChip(/isocortex/i, '.fa-times')
+//       await atlasPage.clickChip(/isocortex/i, '.fa-times')
 
-      const txtArr2 = await atlasPage.getAllChipsText()
-      expect(
-        txtArr2.find(txt => /isocortex/i.test(txt))
-      ).toBeFalsy()
+//       const txtArr2 = await atlasPage.getAllChipsText()
+//       expect(
+//         txtArr2.find(txt => /isocortex/i.test(txt))
+//       ).toBeFalsy()
       
-    })
-  })
-})
+//     })
+//   })
+// })
diff --git a/e2e/src/selecting/region.prod.e2e-spec.js b/e2e/src/selecting/region.prod.e2e-spec.js
index 01b30f83e3db64a1b83854b939403adeec744d3b..9b6e0d535042a9346c0891fe2a5da75d06daee4c 100644
--- a/e2e/src/selecting/region.prod.e2e-spec.js
+++ b/e2e/src/selecting/region.prod.e2e-spec.js
@@ -7,6 +7,7 @@ describe('> selecting regions', () => {
   const duplicatedRegion = {
     atlas: 'Multilevel Human Atlas',
     template: `ICBM 152 2009c Nonlinear Asymmetric`,
+    parcVersion: 'v1.18',
     position: [-0.256, 26.028, -11.678]
   }
   describe(`> when selecting duplicated regions at ${duplicatedRegion.atlas} / ${duplicatedRegion.template} / ${JSON.stringify(duplicatedRegion.position)}`, () => {
@@ -14,7 +15,7 @@ describe('> selecting regions', () => {
       const newPage = new AtlasPage()
       await newPage.init()
       await newPage.goto()
-      await newPage.setAtlasSpecifications(duplicatedRegion.atlas, [ duplicatedRegion.template ])
+      await newPage.setAtlasSpecifications(duplicatedRegion.atlas, [ duplicatedRegion.template ], duplicatedRegion.parcVersion)
       await newPage.wait(500)
       await newPage.waitForAsync()
       await newPage.execScript(`interactiveViewer.viewerHandle.setNavigationLoc(${JSON.stringify(duplicatedRegion.position.map(v => v*1e6))}, true)`)
diff --git a/e2e/src/selecting/template.prod.e2e-spec.js b/e2e/src/selecting/template.prod.e2e-spec.js
index 6009f709fa4ee07450a0c83a96ad74db7bfff7b1..23e378a03c1bc12ceb1994a4bbb5ea7ffe256450 100644
--- a/e2e/src/selecting/template.prod.e2e-spec.js
+++ b/e2e/src/selecting/template.prod.e2e-spec.js
@@ -1,5 +1,12 @@
+const { isatty } = require("tty")
 const { AtlasPage } = require("../util")
 
+const atlasName = 'Multilevel Human Atlas'
+const tNameIcbm152 = 'ICBM 152 2009c Nonlinear Asymmetric'
+// colin has temporarily been disabled
+// const tNameColin = 'MNI Colin 27'
+const tNameBB = 'Big Brain (Histology)'
+
 describe('templates > ', () => {
 
   let iavPage
@@ -12,7 +19,7 @@ describe('templates > ', () => {
     
     it('can select template by clicking main card', async () => {
       await iavPage.goto()
-      await iavPage.selectTitleCard('ICBM 2009c Nonlinear Asymmetric')
+      await iavPage.setAtlasSpecifications(atlasName)
       await iavPage.wait(1000)
   
       const viewerIsPopulated = await iavPage.viewerIsPopulated()
@@ -24,10 +31,10 @@ describe('templates > ', () => {
   
       await iavPage.goto()
   
-      await iavPage.selectTitleCard('ICBM 2009c Nonlinear Asymmetric')
+      await iavPage.setAtlasSpecifications(atlasName, [ tNameIcbm152 ])
       await iavPage.wait(1000)
   
-      await iavPage.selectDropdownTemplate('Big Brain (Histology)')
+      await iavPage.setAtlasSpecifications(atlasName, [ tNameBB ])
       await iavPage.wait(7000)
   
       const viewerIsPopulated = await iavPage.viewerIsPopulated()
@@ -40,10 +47,10 @@ describe('templates > ', () => {
   
       await iavPage.goto()
   
-      await iavPage.selectTitleCard('ICBM 2009c Nonlinear Asymmetric')
+      await iavPage.setAtlasSpecifications(atlasName, [ tNameIcbm152 ])
       await iavPage.wait(1000)
   
-      const info = await iavPage.getTemplateInfo()
+      const info = await iavPage.getAtlasTileInfo(tNameIcbm152)
   
       expect(
         info.indexOf(expectedDesc)
@@ -51,29 +58,51 @@ describe('templates > ', () => {
     })
   })
   
-  describe('switching template > ', () => {
-    it('works in history navigation', async () => {
+  describe('> switching template', () => {
+    beforeEach(async () => {
+
       await iavPage.goto()
-      await iavPage.selectTitleCard('ICBM 2009c Nonlinear Asymmetric')
+      await iavPage.setAtlasSpecifications(atlasName, [ tNameIcbm152 ])
       await iavPage.wait(500)
       await iavPage.waitUntilAllChunksLoaded()
+    })
+    it('> activeFlag works', async () => {
+      const isActive = await iavPage.atlasTileIsActive(tNameIcbm152)
+      expect(isActive.toString()).toEqual('true')
+      const isNotActive = await iavPage.atlasTileIsActive(tNameBB)
+      expect(isNotActive.toString()).toEqual('false')
+
+    })
+    it('> works in regular navigation', async () => {
 
-      await iavPage.selectDropdownTemplate('MNI Colin 27')
+      await iavPage.setAtlasSpecifications(atlasName, [ tNameIcbm152 ])
       await iavPage.wait(500)
       await iavPage.waitUntilAllChunksLoaded()
+      await iavPage.wait(500)
+      
+      const isActive = await iavPage.atlasTileIsActive(tNameIcbm152)
+      expect(isActive.toString()).toEqual('true')
 
-      await iavPage.historyBack()
+      const isNotActive = await iavPage.atlasTileIsActive(tNameBB)
+      expect(isNotActive.toString()).toEqual('false')
+
+    })
+
+    it('> works in history navigation', async () => {
+
+      await iavPage.setAtlasSpecifications(atlasName, [ tNameBB ])
       await iavPage.wait(500)
+      await iavPage.waitUntilAllChunksLoaded()
+      
       await iavPage.historyBack()
-      await iavPage.wait(2000)
-
-      const visible = await iavPage.sideNavIsVisible()
-      if (!visible) await iavPage.clickSideNavTab()
-      const templateInfo = await iavPage.getTemplateInfo()
+      await iavPage.wait(500)
+      await iavPage.waitForAsync()
 
-      expect(
-        templateInfo.indexOf('ICBM 2009c Nonlinear Asymmetric')
-      ).toBeGreaterThanOrEqual(0)
+      const isActive = await iavPage.atlasTileIsActive(tNameIcbm152)
+      expect(isActive.toString()).toEqual('true')
+      const isNotActive = await iavPage.atlasTileIsActive(tNameBB)
+      expect(isNotActive.toString()).toEqual('false')
     })
   })
+
 })
\ No newline at end of file
diff --git a/e2e/util/selenium/base.js b/e2e/util/selenium/base.js
index 8a858a1ebcde66115faaf079b2db32e57c2c2b22..88312daf3cd2eadb52f2b47a390e9d1e6d33c89b 100644
--- a/e2e/util/selenium/base.js
+++ b/e2e/util/selenium/base.js
@@ -39,6 +39,7 @@ const verifyPosition = position => {
 class WdBase{
   constructor() {
     browser.waitForAngularEnabled(false)
+    globalThis.IAVBase = this
   }
   get _browser(){
     return browser
@@ -225,24 +226,15 @@ class WdBase{
 
   async waitForAsync(){
 
-    const checkReady = async () => {
+    await this._browser.wait(async () => {
       const els = await this._browser.findElements(
+        By.css('div.loadingIndicator')
+      )
+      const els2 = await this._browser.findElements(
         By.css('.spinnerAnimationCircle')
       )
-      const visibleEls = []
-      for (const el of els) {
-        if (await el.isDisplayed()) {
-          visibleEls.push(el)
-        }
-      }
-      return !visibleEls.length
-    }
-
-    do {
-      await this.wait(500)
-    } while (
-      !(await retry(checkReady.bind(this), { timeout: 1000, retries: 10 }))
-    )
+      return [...els, ...els2].length === 0
+    }, 1e3 * 60 * 10)
   }
 
   async cursorMoveTo({ position }) {
diff --git a/e2e/util/selenium/iav.js b/e2e/util/selenium/iav.js
index f939460277affef8e097fdb1c487eec6e87b9203..6d75eaa5b7b3ed38e1310d7460fdd24ef874aac2 100644
--- a/e2e/util/selenium/iav.js
+++ b/e2e/util/selenium/iav.js
@@ -17,16 +17,8 @@ class WdIavPage extends WdLayoutPage{
   }
 
   async waitUntilAllChunksLoaded(){
-    await this.waitForCss(`ui-nehuba-container`)
-    await this._browser.wait(async () => {
-      const els = await this._browser.findElements(
-        By.css('div.loadingIndicator')
-      )
-      const els2 = await this._browser.findElements(
-        By.css('.spinnerAnimationCircle')
-      )
-      return [...els, ...els2].length === 0
-    }, 1e3 * 60 * 10)
+    await this.waitForCss(`iav-cmp-viewer-nehuba-glue`)
+    await this.waitForAsync()
   }
 
   async getFloatingCtxInfoAsText(){
@@ -118,6 +110,7 @@ class WdIavPage extends WdLayoutPage{
   }
 
   _getSingleDatasetListView(){
+    throw new Error(`data-browser has been deprecated. rewrite selector`)
     return this._browser
       .findElement( By.css('data-browser') )
       .findElements( By.css('single-dataset-list-view') )
diff --git a/e2e/util/selenium/layout.js b/e2e/util/selenium/layout.js
index dd52470db14b26fc25941e0bb70977c28feb36fb..2b102b6245845b4ec55a57d7b99df3c249292381 100644
--- a/e2e/util/selenium/layout.js
+++ b/e2e/util/selenium/layout.js
@@ -362,23 +362,68 @@ class WdLayoutPage extends WdBase{
     }
   }
 
-  async selectTile(tileName){
+  /**
+   * 
+   * @param {WebElement} webEl 
+   * @description Look for child element fas.fa-info, click, then get modal text
+   */
+  async _getInfo(webEl) {
+    const infoBtn = await webEl.findElement(
+      By.css(`.fas.fa-info`)
+    )
+    if (!infoBtn) {
+      return null
+    } 
+    await infoBtn.click()
+    await this.wait(500)
+    await this.waitForAsync()
+    const text = await this.getModalText()
+    await this.clearAlerts()
+    await this.wait(500)
+    return text
+  }
+
+  async _getAtlasSelectorTile(tileName) {
+
     if (!tileName) throw new Error(`tileName needs to be provided`)
     await this._setAtlasSelectorExpanded(true)
     await this.wait(1000)
     const allTiles = await this._browser.findElements( By.css(`mat-grid-tile`) )
 
     const idx = await _getIndexFromArrayOfWebElements(tileName, allTiles)
-    if (idx >= 0) await allTiles[idx].click()
-    else throw new Error(`#selectTile: tileName ${tileName} cannot be found.`)
+
+    if (idx < 0) throw new Error(`#selectTile: tileName ${tileName} cannot be found.`)
+    return allTiles[idx]
+  }
+
+  async selectTile(tileName){
+    const el = await this._getAtlasSelectorTile(tileName)
+    await el.click()
+  }
+
+  async getAtlasTileInfo(tileName){
+    const el = await this._getAtlasSelectorTile(tileName)
+    return this._getInfo(el)
+  }
+
+  async atlasTileIsActive(tileName) {
+    const el = await this._getAtlasSelectorTile(tileName)
+    return await el.getAttribute('aria-checked')
   }
 
   async changeParc(parcName) {
     throw new Error(`changeParc NYI`)
   }
 
-  async changeParcVersion(parcVerion) {
-    throw new Error(`changeParcVersion NYI`)
+  async changeParcVersion(parcVersion) {
+    await this._browser.findElement(
+      By.css(`[aria-label="${ARIA_LABELS.PARC_VER_SELECT}"]`)
+    ).click()
+    await this.wait(500)
+    this._browser.findElement(
+      By.css(`[aria-label="${ARIA_LABELS.PARC_VER_CONTAINER}"] [aria-label="${parcVersion}"]`)
+    ).click()
+    await this.wait(500)
   }
 
   async setAtlasSpecifications(atlasName, atlasSpecifications = [], parcVersion = null) {
@@ -393,7 +438,7 @@ class WdLayoutPage extends WdBase{
        * if not at title screen
        * select from dropdown
        */
-      console.log(e)
+      console.log(`selecting from ui-splash screen errored, try selecting from dropdown`)
       await this.selectDropdownOption(`[aria-label="${ARIA_LABELS.SELECT_ATLAS}"]`, atlasName)
     }
 
@@ -423,24 +468,24 @@ class WdLayoutPage extends WdBase{
    */
   _getSideNavPrimary(){
     return this._browser.findElement(
-      By.css('mat-drawer[data-mat-drawer-primary-open]')
+      By.css('mat-drawer[data-mat-drawer-top-open]')
     )
   }
 
   async _getSideNavPrimaryExpanded(){
     return (await this._getSideNavPrimary()
-      .getAttribute('data-mat-drawer-primary-open')) === 'true'
+      .getAttribute('data-mat-drawer-top-open')) === 'true'
   }
 
   _getSideNavSecondary(){
     return this._browser.findElement(
-      By.css('mat-drawer[data-mat-drawer-secondary-open]')
+      By.css('mat-drawer[data-mat-drawer-fullleft-open]')
     )
   }
 
   async _getSideNavSecondaryExpanded(){
     return (await this._getSideNavSecondary()
-      .getAttribute('data-mat-drawer-secondary-open')) === 'true'
+      .getAttribute('data-mat-drawer-fullleft-open')) === 'true'
   }
 
   async _setSideNavPrimaryExpanded(flag) {
@@ -494,10 +539,6 @@ class WdLayoutPage extends WdBase{
     return this._getStatusPanel().click()
   }
 
-  async getTemplateInfo(){
-    throw new Error(`getTemplateInfo has been deprecated. Implmenet new method of getting info`)
-  }
-
   // will throw if additional layer control is not visible
   additionalLayerControlIsExpanded() {
     throw new Error(`additionalLayerControlIsExpanded is deprecated`)
diff --git a/e2e_env.md b/e2e_env.md
new file mode 100644
index 0000000000000000000000000000000000000000..0323b6a621a52a11aa6e2b4783d7452ce748b0da
--- /dev/null
+++ b/e2e_env.md
@@ -0,0 +1,7 @@
+# End-to-end Tests Environment Variables
+
+| name | description | default | example | 
+| --- | --- | --- | --- |
+| PROTRACTOR_SPECS | specs relative to `./e2e/` | `./src/**/*.prod.e2e-spec.js` |  |
+| DISABLE_CHROME_HEADLESS | disable headless chrome, spawns chrome window | `unset` (falsy) | 1 |
+| ENABLE_GPU | uses GPU. nb, in headless mode, will show requirement not met | `unset` (falsy) | 1 |
diff --git a/mkdocs.yml b/mkdocs.yml
index ce26a24795110258e2225efb24c1a75c72d0cbb5..b09aa94d70dfd245a9b73c6a02d529d4bd720f1b 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.0: 'releases/v2.4.0.md'
     - v2.3.11: 'releases/v2.3.11.md'
     - v2.3.10: 'releases/v2.3.10.md'
     - v2.3.9: 'releases/v2.3.9.md'
diff --git a/package.json b/package.json
index 817cacfec3c0863d9f0fec4eb2865ed3ee6ce3a9..b35b456f28f9dbd58e26dd6cf04a53a025f4c52c 100644
--- a/package.json
+++ b/package.json
@@ -1,17 +1,10 @@
 {
   "name": "interactive-viewer",
-  "version": "2.3.11",
-  "description": "HBP interactive atlas viewer. Integrating KG query, dataset previews & more. Based on humanbrainproject/nehuba & google/neuroglancer. Built with angular.io",
+  "version": "2.4.0",
+  "description": "HBP interactive atlas viewer. Integrating KG query, dataset previews & more. Based on humanbrainproject/nehuba & google/neuroglancer. Built with angular",
   "scripts": {
-    "dev-server-export": "webpack-dev-server --config webpack.export.js",
-    "build-export": "webpack --config webpack.export.js",
-    "build-export-min": "webpack --config webpack.export.min.js",
-    "build-export-aot": "webpack --config webpack.export.aot.js",
-    "build-aot": "PRODUCTION=true GIT_HASH=`git rev-parse --short HEAD` webpack --config webpack.aot.js && node ./third_party/matomo/processMatomo.js",
-    "plugin-server": "node ./src/plugin_examples/server.js",
-    "dev-server": "BACKEND_URL=${BACKEND_URL:-http://localhost:3000/} webpack-dev-server --config webpack.dev.js --mode development",
-    "dev-server-aot": "BACKEND_URL=${BACKEND_URL:-http://localhost:3000/} PRODUCTION=true GIT_HASH=`git log --pretty=format:'%h' --invert-grep --grep=^.ignore -1` webpack-dev-server --config webpack.dev-aot.js",
-    "dev-server-all-interfaces": "webpack-dev-server --config webpack.dev.js --mode development --hot --host 0.0.0.0",
+    "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",
+    "dev-server-aot": "BACKEND_URL=${BACKEND_URL:-http://localhost:3000/} PRODUCTION=true GIT_HASH=`node -e 'console.log(require(\"./package.json\").version)'` webpack-dev-server --config ./webpack/webpack.dev-aot.js",
     "test": "karma start spec/karma.conf.js",
     "e2e": "protractor e2e/protractor.conf",
     "lint": "eslint src --ext .ts",
@@ -51,7 +44,6 @@
     "glob": "^7.1.6",
     "hammerjs": "^2.0.8",
     "html-webpack-plugin": "^3.2.0",
-    "html2canvas": "^1.0.0-rc.1",
     "jasmine": "^3.1.0",
     "jasmine-core": "^3.5.0",
     "jasmine-marbles": "^0.6.0",
@@ -80,15 +72,17 @@
     "webpack": "^4.41.2",
     "webpack-cli": "^3.3.2",
     "webpack-closure-compiler": "^2.1.6",
-    "webpack-dev-server": "^3.10.3",
+    "webpack-dev-server": "^3.11.2",
     "webpack-merge": "^4.1.2"
   },
   "dependencies": {
+    "@angular/router": "^9.1.13",
     "@ngrx/effects": "^9.1.1",
     "@ngrx/store": "^9.1.1",
     "@types/node": "12.12.39",
-    "export-nehuba": "0.0.4",
-    "hbp-connectivity-component": "^0.3.18",
+    "export-nehuba": "0.0.12",
+    "hbp-connectivity-component": "^0.4.4",
+    "jszip": "^3.6.0",
     "zone.js": "^0.10.2"
   }
 }
diff --git a/spec/karma.conf.js b/spec/karma.conf.js
index 4f4eb985a36341172d86343a1b95e194e9300bbc..76c12d0cbd668862a62d60ff9377427005964bc1 100644
--- a/spec/karma.conf.js
+++ b/spec/karma.conf.js
@@ -2,8 +2,8 @@
 // Generated on Mon Aug 06 2018 12:37:42 GMT+0200 (CEST)
 
 const merge = require('webpack-merge')
-const webpackTest = require('../webpack.test')
-const webpackConfig = require('../webpack.dev')
+const webpackTest = require('../webpack/webpack.test')
+const webpackConfig = require('../webpack/webpack.dev')
 const fullWebpack = merge(webpackTest, webpackConfig)
 
 const singleRun = process.env.NODE_ENV === 'test'
@@ -37,7 +37,7 @@ module.exports = function(config) {
         included: true
       },
       {
-        pattern: './src/util/worker.js',
+        pattern: './worker/worker.js',
         served: true,
         included: false
       },
diff --git a/spec/test.ts b/spec/test.ts
index 16b7c55003580b580c77285ae944fbb7a61538fc..478be25bbf91e4b0d442d079f8bc81bd0d868493 100644
--- a/spec/test.ts
+++ b/spec/test.ts
@@ -21,4 +21,7 @@ getTestBed().initTestEnvironment(
 const testContext = require.context('../src', true, /\.spec\.ts$/)
 testContext.keys().map(testContext)
 
+const workerCtx = require.context('../worker', true, /\.spec\.js$/)
+workerCtx.keys().map(workerCtx)
+
 require('../common/util.spec.js')
diff --git a/src/atlasComponents/connectivity/connectivityBrowser/connectivityBrowser.component.spec.ts b/src/atlasComponents/connectivity/connectivityBrowser/connectivityBrowser.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6a7449787611327577ac114c5060b7abffb6a9d8
--- /dev/null
+++ b/src/atlasComponents/connectivity/connectivityBrowser/connectivityBrowser.component.spec.ts
@@ -0,0 +1,113 @@
+import {ConnectivityBrowserComponent} from "./connectivityBrowser.component";
+import {async, ComponentFixture, TestBed} from "@angular/core/testing";
+import {Action} from "@ngrx/store";
+import {HttpClientModule} from "@angular/common/http";
+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 { viewerStateAllRegionsFlattenedRegionSelector, viewerStateOverwrittenColorMapSelector } from "src/services/state/viewerState/selectors";
+import { ngViewerSelectorClearViewEntries } from "src/services/state/ngViewerState.store.helper";
+import {BS_ENDPOINT} from "src/util/constants";
+
+/**
+ * injecting databrowser module is bad idea
+ * since it relies on its own selectors
+ * since the only reason why data browser is imported is to use show dataset dialogue
+ * just use a dummy directive
+ */
+const MOCK_BS_ENDPOINT = `http://localhost:1234`
+
+@Directive({
+    selector: '[iav-dataset-show-dataset-dialog]'
+})
+
+class DummyDirective{
+    @Input('iav-dataset-show-dataset-dialog-name')
+    name: string
+    @Input('iav-dataset-show-dataset-dialog-description')
+    description: string
+    @Input('iav-dataset-show-dataset-dialog-kgid')
+    kgId: string
+    @Input('iav-dataset-show-dataset-dialog-kgschema')
+    kgSchema: string
+}
+
+describe('ConnectivityComponent', () => {
+
+    let component: ConnectivityBrowserComponent;
+    let fixture: ComponentFixture<ConnectivityBrowserComponent>;
+    const actions$: Observable<Action> = of({type: 'TEST'})
+
+    let datasetList = [
+        {
+            ['@id']: 'id1',
+            src_name: 'id1',
+            src_info: 'd1',
+            kgId: 'kgId1',
+            kgschema: 'kgschema1'
+        }, {
+            ['@id']: 'id2',
+            src_name: 'id2',
+            src_info: 'd2',
+            kgId: 'kgId2',
+            kgschema: 'kgschema2'
+        }
+    ]
+
+    beforeEach(async (() => {
+        TestBed.configureTestingModule({
+            imports: [
+                HttpClientModule,
+            ],
+            providers: [
+                provideMockActions(() => actions$),
+                provideMockStore(),
+                {
+                    provide: BS_ENDPOINT,
+                    useValue: MOCK_BS_ENDPOINT
+                }
+            ],
+            declarations: [
+                ConnectivityBrowserComponent,
+                DummyDirective,
+            ],
+            schemas: [
+                CUSTOM_ELEMENTS_SCHEMA,
+            ],
+        }).compileComponents()
+
+    }));
+
+    beforeEach(() => {
+        const mockStore = TestBed.inject(MockStore)
+        mockStore.overrideSelector(viewerStateAllRegionsFlattenedRegionSelector, [])
+        mockStore.overrideSelector(viewerStateOverwrittenColorMapSelector, null)
+        mockStore.overrideSelector(ngViewerSelectorClearViewEntries, [])
+    })
+
+    it('> component can be created', async () => {
+        fixture = TestBed.createComponent(ConnectivityBrowserComponent)
+        component = fixture.componentInstance
+        expect(component).toBeTruthy()
+    })
+
+    // ToDo create test for kgId and kgSchema after it will work while viewing dataset
+    it('> change dataset changes name and description', () => {
+        fixture = TestBed.createComponent(ConnectivityBrowserComponent)
+        component = fixture.componentInstance
+
+        component.datasetList = datasetList
+
+        component.changeDataset({value: 'id1'})
+
+        expect(component.selectedDataset).toEqual('id1')
+        expect(component.selectedDatasetDescription).toEqual('d1')
+
+        component.changeDataset({value: 'id2'})
+
+        expect(component.selectedDataset).toEqual('id2')
+        expect(component.selectedDatasetDescription).toEqual('d2')
+    })
+
+});
diff --git a/src/ui/connectivityBrowser/connectivityBrowser.component.ts b/src/atlasComponents/connectivity/connectivityBrowser/connectivityBrowser.component.ts
similarity index 76%
rename from src/ui/connectivityBrowser/connectivityBrowser.component.ts
rename to src/atlasComponents/connectivity/connectivityBrowser/connectivityBrowser.component.ts
index 5e1996b84472dcfac7b6f78a57ec76f7b0d8960d..f7573cfb517e00266c8c02cee7cbd271dc30880b 100644
--- a/src/ui/connectivityBrowser/connectivityBrowser.component.ts
+++ b/src/atlasComponents/connectivity/connectivityBrowser/connectivityBrowser.component.ts
@@ -7,31 +7,35 @@ import {
   Output,
   ViewChild,
   Input,
-  OnInit,
+  OnInit, Inject,
 } from "@angular/core";
 import {select, Store} from "@ngrx/store";
 import {fromEvent, Observable, Subscription, Subject, combineLatest} from "rxjs";
 import {distinctUntilChanged, filter, map} from "rxjs/operators";
-import {
-  CLEAR_CONNECTIVITY_REGION,
-  SELECT_REGIONS,
-  SET_OVERWRITTEN_COLOR_MAP
-} from "src/services/state/viewerState.store";
-import {safeFilter} from "src/services/stateStore.service";
-import {viewerStateNavigateToRegion} from "src/services/state/viewerState.store.helper";
-import {ngViewerActionClearView} from "src/services/state/ngViewerState/actions";
-import {ngViewerSelectorClearViewEntries} from "src/services/state/ngViewerState/selectors";
+
+import { viewerStateNavigateToRegion, viewerStateSetSelectedRegions } from "src/services/state/viewerState.store.helper";
+import { ngViewerSelectorClearViewEntries, ngViewerActionClearView } from "src/services/state/ngViewerState.store.helper";
 import {
   viewerStateAllRegionsFlattenedRegionSelector,
   viewerStateOverwrittenColorMapSelector
 } from "src/services/state/viewerState/selectors";
 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";
+
 
 const CONNECTIVITY_NAME_PLATE = 'Connectivity'
 
 @Component({
   selector: 'connectivity-browser',
   templateUrl: './connectivityBrowser.template.html',
+  providers: [
+    {
+      provide: OVERWRITE_SHOW_DATASET_DIALOG_TOKEN,
+      useValue: null
+    }
+  ]
 })
 export class ConnectivityBrowserComponent implements OnInit, AfterViewInit, OnDestroy {
 
@@ -44,7 +48,7 @@ export class ConnectivityBrowserComponent implements OnInit, AfterViewInit, OnDe
      */
     private _isFirstUpdate = true
 
-    public connectivityUrl = 'https://connectivity-query-v1-1-connectivity.apps.hbp.eu/v1.1/studies'
+    public connectivityUrl: string
 
     private accordionIsExpanded = false
 
@@ -66,11 +70,20 @@ export class ConnectivityBrowserComponent implements OnInit, AfterViewInit, OnDe
         })
       )
       this.store$.dispatch({
-        type: SET_OVERWRITTEN_COLOR_MAP,
+        type: 'SET_OVERWRITTEN_COLOR_MAP',
         payload: flag? CONNECTIVITY_NAME_PLATE : false,
       })
+
+      if (flag) {
+        this.addNewColorMap()
+      } else {
+        this.restoreDefaultColormap()
+      }
     }
 
+    @Output()
+    connectivityDataReceived = new EventEmitter<any>()
+
     @Output()
     setOpenState: EventEmitter<boolean> = new EventEmitter()
 
@@ -85,7 +98,7 @@ export class ConnectivityBrowserComponent implements OnInit, AfterViewInit, OnDe
 
       if (!val) {
         this.store$.dispatch({
-          type: SET_OVERWRITTEN_COLOR_MAP,
+          type: 'SET_OVERWRITTEN_COLOR_MAP',
           payload: false,
         })
         return
@@ -102,11 +115,20 @@ 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()
+      }
       // TODO may not be necessary
       this.changeDetectionRef.detectChanges()
     }
-    @Input() parcellationId: any
+    public atlasId: any
+    public parcellationId: any
+    public regionId: string
     public regionName: string
     public regionHemisphere: string = null
     public datasetList: any[] = []
@@ -134,13 +156,12 @@ export class ConnectivityBrowserComponent implements OnInit, AfterViewInit, OnDe
     constructor(
         private store$: Store<any>,
         private changeDetectionRef: ChangeDetectorRef,
-        private httpClient: HttpClient
+        private httpClient: HttpClient,
+        @Inject(BS_ENDPOINT) private siibraApiUrl: string,
     ) {
 
       this.overwrittenColorMap$ = this.store$.pipe(
-        select('viewerState'),
-        safeFilter('overwrittenColorMap'),
-        map(state => state.overwrittenColorMap),
+        select(viewerStateOverwrittenColorMapSelector),
         distinctUntilChanged()
       )
     }
@@ -149,12 +170,15 @@ export class ConnectivityBrowserComponent implements OnInit, AfterViewInit, OnDe
     public fullConnectivityLoadUrl: string
 
     ngOnInit(): void {
+      this.setConnectivityUrl()
+
       this.httpClient.get<[]>(this.connectivityUrl).subscribe(res => {
-        this.datasetList = res.filter(dl => dl['parcellation id'] === this.parcellationId)
-        this.selectedDataset = this.datasetList[0].name
-        this.selectedDatasetDescription = this.datasetList[0].description
-        this.selectedDatasetKgId = this.datasetList[0].kgId
-        this.selectedDatasetKgSchema = this.datasetList[0].kgschema
+        this.datasetList = res
+        this.selectedDataset = this.datasetList[0]?.['@id']
+        this.selectedDatasetDescription = this.datasetList[0]?.['src_info']
+        // this.selectedDatasetKgId = this.datasetList[0]?.kgId || null
+        // this.selectedDatasetKgSchema = this.datasetList[0]?.kgSchema || null
+
         this.changeDataset()
       })
     }
@@ -213,7 +237,12 @@ export class ConnectivityBrowserComponent implements OnInit, AfterViewInit, OnDe
             distinctUntilChanged()
           ),
           fromEvent(this.connectivityComponentElement?.nativeElement, 'connectivityDataReceived').pipe(
-            map((e: CustomEvent) => e.detail)
+            map((e: CustomEvent) => {
+              if (e.detail !== 'No data') {
+                this.connectivityNumberReceived.emit(e.detail.length)
+              }
+              return e.detail
+            })
           )
         ).subscribe(([flag, connectedAreas]) => {
           if (connectedAreas === 'No data') {
@@ -228,26 +257,20 @@ export class ConnectivityBrowserComponent implements OnInit, AfterViewInit, OnDe
               })
             )
             this.noDataReceived = false
-
             this.connectivityNumberReceived.emit(connectedAreas.length)
             this.connectedAreas = connectedAreas
 
             if (flag) {
               this.addNewColorMap()
               this.store$.dispatch({
-                type: SET_OVERWRITTEN_COLOR_MAP,
+                type: 'SET_OVERWRITTEN_COLOR_MAP',
                 payload: 'connectivity',
               })
             } else {
               this.restoreDefaultColormap()
 
-              this.store$.dispatch({type: SET_OVERWRITTEN_COLOR_MAP, payload: null})
+              this.store$.dispatch({type: 'SET_OVERWRITTEN_COLOR_MAP', payload: null})
 
-              /**
-                         * TODO
-                         * may no longer be necessary
-                         */
-              this.store$.dispatch({type: CLEAR_CONNECTIVITY_REGION})
             }
           }
         })
@@ -282,6 +305,16 @@ export class ConnectivityBrowserComponent implements OnInit, AfterViewInit, OnDe
       this.subscriptions.forEach(s => s.unsubscribe())
     }
 
+    private setConnectivityUrl() {
+      this.connectivityUrl = `${this.siibraApiUrl}/atlases/${encodeURIComponent(this.atlasId)}/parcellations/${encodeURIComponent(this.parcellationId)}/regions/${encodeURIComponent(this.regionId || 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({
@@ -300,18 +333,15 @@ export class ConnectivityBrowserComponent implements OnInit, AfterViewInit, OnDe
     changeDataset(event = null) {
       if (event) {
         this.selectedDataset = event.value
-        const foundDataset = this.datasetList.find(d => d.name === this.selectedDataset)
-        this.selectedDatasetDescription = foundDataset?.description
-        this.selectedDatasetKgId = foundDataset?.kgId || null
-        this.selectedDatasetKgSchema = foundDataset?.kgschema || null
+        const foundDataset = this.datasetList.find(d => d['@id'] === this.selectedDataset)
+        this.selectedDatasetDescription = foundDataset?.['src_info']
+        // this.selectedDatasetKgId = foundDataset?.kgId || null
+        // this.selectedDatasetKgSchema = foundDataset?.kgSchema || null
       }
       if (this.datasetList.length && this.selectedDataset) {
-        const selectedDatasetId = this.datasetList.find(d => d.name === this.selectedDataset).id
-        const url = selectedDatasetId ? `${this.connectivityUrl}/${selectedDatasetId}` : null
-        this.connectivityLoadUrl.emit(url)
-        this.loadUrl = url
+        this.setProfileLoadUrl()
 
-        this.fullConnectivityLoadUrl = selectedDatasetId ? `${this.connectivityUrl}/${selectedDatasetId}/full_matrix` : null
+        this.fullConnectivityLoadUrl = `${this.siibraApiUrl}/atlases/${encodeURIComponent(this.atlasId)}/parcellations/${encodeURIComponent(this.parcellationId)}/features/ConnectivityMatrix/${encodeURIComponent(this.selectedDataset)}`
       }
     }
 
@@ -324,10 +354,11 @@ export class ConnectivityBrowserComponent implements OnInit, AfterViewInit, OnDe
     }
 
     selectRegion(region) {
-      this.store$.dispatch({
-        type: SELECT_REGIONS,
-        selectRegions: [region],
-      })
+      this.store$.dispatch(
+        viewerStateSetSelectedRegions({
+          selectRegions: [ region ]
+        })
+      )
     }
 
     getRegionWithName(region) {
@@ -356,7 +387,7 @@ export class ConnectivityBrowserComponent implements OnInit, AfterViewInit, OnDe
 
     public addNewColorMap() {
       if (!this.defaultColorMap) {
-        this.defaultColorMap = new Map(getWindow().interactiveViewer.viewerHandle.getLayersSegmentColourMap())
+        this.defaultColorMap = getWindow().interactiveViewer.viewerHandle.getLayersSegmentColourMap()
       }
 
       const existingMap: Map<string, Map<number, { red: number, green: number, blue: number }>> = (getWindow().interactiveViewer.viewerHandle.getLayersSegmentColourMap())
diff --git a/src/ui/connectivityBrowser/connectivityBrowser.template.html b/src/atlasComponents/connectivity/connectivityBrowser/connectivityBrowser.template.html
similarity index 91%
rename from src/ui/connectivityBrowser/connectivityBrowser.template.html
rename to src/atlasComponents/connectivity/connectivityBrowser/connectivityBrowser.template.html
index eafa8d10c31ff86138e9d45cff13cea3201aaf43..696e579280a4b119a33ae589417bc282246cd634 100644
--- a/src/ui/connectivityBrowser/connectivityBrowser.template.html
+++ b/src/atlasComponents/connectivity/connectivityBrowser/connectivityBrowser.template.html
@@ -1,5 +1,6 @@
 <div class="w-100 h-100 d-block d-flex flex-column pb-2">
     <hbp-connectivity-matrix-row
+            (connectivityDataReceived)="connectivityDataReceived.emit($event)"
             #connectivityComponent
             *ngIf="regionName"
             [region]="regionName + (regionHemisphere? ' - ' + regionHemisphere : '')"
@@ -29,13 +30,14 @@
                                 (selectionChange)="changeDataset($event)">
                             <mat-option
                                     *ngFor="let dataset of datasetList"
-                                    [value]="dataset.name">
-                                {{ dataset.name }}
+                                    [value]="dataset['@id']">
+                                {{ dataset['src_name'] }}
                             </mat-option>
                         </mat-select>
                     </mat-form-field>
-                <ng-container *ngIf="selectedDataset && selectedDatasetDescription" >
-                    <button class="flex-grow-0 flex-shrink-0" mat-icon-button
+                <ng-container *ngIf="selectedDataset && (selectedDatasetDescription || selectedDatasetKgId)" >
+                    <button class="flex-grow-0 flex-shrink-0"
+                            mat-icon-button
                             iav-dataset-show-dataset-dialog
                             [iav-dataset-show-dataset-dialog-name]="selectedDataset"
                             [iav-dataset-show-dataset-dialog-description]="selectedDatasetDescription"
diff --git a/src/atlasComponents/connectivity/hasConnectivity.directive.ts b/src/atlasComponents/connectivity/hasConnectivity.directive.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b3cf77233d0a1a991722b42876cc38c60c7834ca
--- /dev/null
+++ b/src/atlasComponents/connectivity/hasConnectivity.directive.ts
@@ -0,0 +1,58 @@
+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) {
+      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
new file mode 100644
index 0000000000000000000000000000000000000000..c9a2e808fc6290283e77ea4bccff8f199eea3ca0
--- /dev/null
+++ b/src/atlasComponents/connectivity/index.ts
@@ -0,0 +1,2 @@
+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
new file mode 100644
index 0000000000000000000000000000000000000000..f8fa66e0e5378141834f3696091ad3227a367bbf
--- /dev/null
+++ b/src/atlasComponents/connectivity/module.ts
@@ -0,0 +1,27 @@
+import { CommonModule } from "@angular/common";
+import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from "@angular/core";
+import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module";
+import { ConnectivityBrowserComponent } from "./connectivityBrowser/connectivityBrowser.component";
+import {HasConnectivity} from "src/atlasComponents/connectivity/hasConnectivity.directive";
+import {KgDatasetModule} from "src/atlasComponents/regionalFeatures/bsFeatures/kgDataset";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    KgDatasetModule,
+    AngularMaterialModule
+  ],
+  declarations: [
+    ConnectivityBrowserComponent,
+    HasConnectivity
+  ],
+  exports: [
+    ConnectivityBrowserComponent,
+    HasConnectivity
+  ],
+  schemas: [
+    CUSTOM_ELEMENTS_SCHEMA,
+  ],
+})
+
+export class AtlasCmptConnModule{}
diff --git a/src/ui/databrowserModule/bulkDownload/bulkDownloadBtn.component.ts b/src/atlasComponents/databrowserModule/bulkDownload/bulkDownloadBtn.component.ts
similarity index 100%
rename from src/ui/databrowserModule/bulkDownload/bulkDownloadBtn.component.ts
rename to src/atlasComponents/databrowserModule/bulkDownload/bulkDownloadBtn.component.ts
diff --git a/src/ui/databrowserModule/bulkDownload/bulkDownloadBtn.template.html b/src/atlasComponents/databrowserModule/bulkDownload/bulkDownloadBtn.template.html
similarity index 100%
rename from src/ui/databrowserModule/bulkDownload/bulkDownloadBtn.template.html
rename to src/atlasComponents/databrowserModule/bulkDownload/bulkDownloadBtn.template.html
diff --git a/src/ui/databrowserModule/constants.ts b/src/atlasComponents/databrowserModule/constants.ts
similarity index 100%
rename from src/ui/databrowserModule/constants.ts
rename to src/atlasComponents/databrowserModule/constants.ts
diff --git a/src/ui/databrowserModule/contributor/index.ts b/src/atlasComponents/databrowserModule/contributor/index.ts
similarity index 100%
rename from src/ui/databrowserModule/contributor/index.ts
rename to src/atlasComponents/databrowserModule/contributor/index.ts
diff --git a/src/ui/databrowserModule/contributor/kgLink.pipe.ts b/src/atlasComponents/databrowserModule/contributor/kgLink.pipe.ts
similarity index 100%
rename from src/ui/databrowserModule/contributor/kgLink.pipe.ts
rename to src/atlasComponents/databrowserModule/contributor/kgLink.pipe.ts
diff --git a/src/ui/databrowserModule/contributor/module.ts b/src/atlasComponents/databrowserModule/contributor/module.ts
similarity index 100%
rename from src/ui/databrowserModule/contributor/module.ts
rename to src/atlasComponents/databrowserModule/contributor/module.ts
diff --git a/src/ui/databrowserModule/contributor/util.ts b/src/atlasComponents/databrowserModule/contributor/util.ts
similarity index 100%
rename from src/ui/databrowserModule/contributor/util.ts
rename to src/atlasComponents/databrowserModule/contributor/util.ts
diff --git a/src/ui/databrowserModule/databrowser.module.ts b/src/atlasComponents/databrowserModule/databrowser.module.ts
similarity index 72%
rename from src/ui/databrowserModule/databrowser.module.ts
rename to src/atlasComponents/databrowserModule/databrowser.module.ts
index b03aec66953c43b10a40dca97a1f308b85f4cfcd..e105fdb3bcb13450b2a9e1f08fc90bb6cfbaa0f0 100644
--- a/src/ui/databrowserModule/databrowser.module.ts
+++ b/src/atlasComponents/databrowserModule/databrowser.module.ts
@@ -4,10 +4,7 @@ import { FormsModule } from "@angular/forms";
 import { ComponentsModule } from "src/components/components.module";
 import { AngularMaterialModule } from 'src/ui/sharedModules/angularMaterial.module'
 import { UtilModule } from "src/util";
-import { DataBrowser } from "./databrowser/databrowser.component";
 import { KgSingleDatasetService } from "./kgSingleDatasetService.service"
-import { ModalityPicker, SortModalityAlphabeticallyPipe } from "./modalityPicker/modalityPicker.component";
-import { SingleDatasetView } from './singleDataset/detailedView/singleDataset.component'
 import { AggregateArrayIntoRootPipe } from "./util/aggregateArrayIntoRoot.pipe";
 import { CopyPropertyPipe } from "./util/copyProperty.pipe";
 import { DatasetIsFavedPipe } from "./util/datasetIsFaved.pipe";
@@ -20,14 +17,11 @@ import { ScrollingModule } from "@angular/cdk/scrolling";
 import { PreviewFileIconPipe } from "./preview/previewFileIcon.pipe";
 import { PreviewFileTypePipe } from "./preview/previewFileType.pipe";
 import { SingleDatasetListView } from "./singleDataset/listView/singleDatasetListView.component";
-import { AppendFilerModalityPipe } from "./util/appendFilterModality.pipe";
 import { GetKgSchemaIdFromFullIdPipe, getKgSchemaIdFromFullId } from "./util/getKgSchemaIdFromFullId.pipe";
-import { ResetCounterModalityPipe } from "./util/resetCounterModality.pipe";
 import { PreviewFileVisibleInSelectedReferenceTemplatePipe } from "./util/previewFileDisabledByReferenceSpace.pipe";
 import { DatasetPreviewList, UnavailableTooltip } from "./preview/datasetPreviews/datasetPreviewsList/datasetPreviewList.component";
 import { PreviewComponentWrapper } from "./preview/previewComponentWrapper/previewCW.component";
 import { BulkDownloadBtn, TransformDatasetToIdPipe } from "./bulkDownload/bulkDownloadBtn.component";
-import { ShowDatasetDialogDirective, IAV_DATASET_SHOW_DATASET_DIALOG_CMP } from "./showDatasetDialog.directive";
 import { PreviewDatasetFile, IAV_DATASET_PREVIEW_DATASET_FN, IAV_DATASET_PREVIEW_ACTIVE, TypePreviewDispalyed } from "./preview/previewDatasetFile.directive";
 
 import {
@@ -38,16 +32,13 @@ import {
   OVERRIDE_IAV_DATASET_PREVIEW_DATASET_FN,
 } from './constants'
 import { ShownPreviewsDirective } from "./preview/shownPreviews.directive";
-import { FilterPreviewByType } from "./preview/filterPreview.pipe";
-import { PreviewCardComponent } from "./preview/previewCard/previewCard.component";
-import { LayerBrowserModule } from "../layerbrowser";
-import { DatabrowserDirective } from "./databrowser/databrowser.directive";
+import { LayerBrowserModule } from "../../ui/layerbrowser";
+
 import { ContributorModule } from "./contributor";
 import { DatabrowserService } from "./databrowser.service";
-import { ShownDatasetDirective } from "./shownDataset.directive";
-import { SingleDatasetSideNavView } from "./singleDataset/sideNavView/sDsSideNavView.component";
 import { RegionalFeaturesModule } from "../regionalFeatures";
 import { SingleDatasetDirective } from "./singleDataset/singleDataset.directive";
+import { KgDatasetModule } from "../regionalFeatures/bsFeatures/kgDataset";
 
 
 const previewEmitFactory = ( overrideFn: (file: any, dataset: any) => void) => {
@@ -55,6 +46,9 @@ const previewEmitFactory = ( overrideFn: (file: any, dataset: any) => void) => {
   return () => console.error(`previewEmitFactory not overriden`)
 }
 
+/**
+ * TODO deprecate
+ */
 @NgModule({
   imports: [
     CommonModule,
@@ -66,27 +60,20 @@ const previewEmitFactory = ( overrideFn: (file: any, dataset: any) => void) => {
     LayerBrowserModule,
     ContributorModule,
     RegionalFeaturesModule,
+    KgDatasetModule,
   ],
   declarations: [
-    DataBrowser,
-    ModalityPicker,
-    SingleDatasetView,
     SingleDatasetDirective,
     SingleDatasetListView,
     DatasetPreviewList,
     PreviewComponentWrapper,
     BulkDownloadBtn,
-    PreviewCardComponent,
-    SingleDatasetSideNavView,
 
     /**
      * Directives
      */
-    ShowDatasetDialogDirective,
     PreviewDatasetFile,
     ShownPreviewsDirective,
-    DatabrowserDirective,
-    ShownDatasetDirective,
 
     /**
      * pipes
@@ -101,47 +88,30 @@ const previewEmitFactory = ( overrideFn: (file: any, dataset: any) => void) => {
     GetKgSchemaIdFromFullIdPipe,
     PreviewFileIconPipe,
     PreviewFileTypePipe,
-    AppendFilerModalityPipe,
-    ResetCounterModalityPipe,
     PreviewFileVisibleInSelectedReferenceTemplatePipe,
     UnavailableTooltip,
     TransformDatasetToIdPipe,
-    SortModalityAlphabeticallyPipe,
     PreviewFileTypePipe,
-    FilterPreviewByType,
   ],
   exports: [
-    DataBrowser,
-    SingleDatasetView,
+    KgDatasetModule,
     SingleDatasetDirective,
     SingleDatasetListView,
-    ModalityPicker,
     FilterDataEntriesbyMethods,
     GetKgSchemaIdFromFullIdPipe,
     BulkDownloadBtn,
     TransformDatasetToIdPipe,
-    ShowDatasetDialogDirective,
     PreviewDatasetFile,
     PreviewFileTypePipe,
     ShownPreviewsDirective,
-    FilterPreviewByType,
-    PreviewCardComponent,
-    DatabrowserDirective,
-    ShownDatasetDirective,
-    SingleDatasetSideNavView,
   ],
   entryComponents: [
-    DataBrowser,
-    SingleDatasetView,
     PreviewComponentWrapper
   ],
   providers: [
     KgSingleDatasetService,
     DatabrowserService,
     {
-      provide: IAV_DATASET_SHOW_DATASET_DIALOG_CMP,
-      useValue: SingleDatasetView
-    },{
       provide: IAV_DATASET_PREVIEW_DATASET_FN,
       useFactory: previewEmitFactory,
       deps: [ [new Optional(), OVERRIDE_IAV_DATASET_PREVIEW_DATASET_FN] ]
diff --git a/src/ui/databrowserModule/databrowser.service.spec.ts b/src/atlasComponents/databrowserModule/databrowser.service.spec.ts
similarity index 100%
rename from src/ui/databrowserModule/databrowser.service.spec.ts
rename to src/atlasComponents/databrowserModule/databrowser.service.spec.ts
diff --git a/src/ui/databrowserModule/databrowser.service.ts b/src/atlasComponents/databrowserModule/databrowser.service.ts
similarity index 75%
rename from src/ui/databrowserModule/databrowser.service.ts
rename to src/atlasComponents/databrowserModule/databrowser.service.ts
index 4ae965640e82ecd887e29d662949cb1bda09b71f..31fd9b517d5818e168e0d47ce73dc64fdfe44159 100644
--- a/src/ui/databrowserModule/databrowser.service.ts
+++ b/src/atlasComponents/databrowserModule/databrowser.service.ts
@@ -3,23 +3,24 @@ import {ComponentRef, Injectable, OnDestroy} from "@angular/core";
 import { select, Store } from "@ngrx/store";
 import { BehaviorSubject, forkJoin, from, fromEvent, Observable, of, Subscription } from "rxjs";
 import { catchError, debounceTime, distinctUntilChanged, filter, map, shareReplay, switchMap, withLatestFrom } from "rxjs/operators";
-import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service";
 import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service";
 
 // TODO remove dependency on widget unit module
 import { WidgetUnit } from "src/widget";
 
 import { LoggingService } from "src/logging";
-import { SHOW_KG_TOS } from "src/services/state/uiState.store";
-import { FETCHED_DATAENTRIES, FETCHED_SPATIAL_DATA, IavRootStoreInterface, IDataEntry, safeFilter } from "src/services/stateStore.service";
-import { DataBrowser } from "./databrowser/databrowser.component";
+import { SHOW_KG_TOS } from "src/services/state/uiState.store.helper";
+
 import { NO_METHODS } from "./util/filterDataEntriesByMethods.pipe";
 import { FilterDataEntriesByRegion } from "./util/filterDataEntriesByRegion.pipe";
-import { datastateActionToggleFav, datastateActionUnfavDataset, datastateActionFavDataset } from "src/services/state/dataState/actions";
+import { datastateActionToggleFav, datastateActionUnfavDataset, datastateActionFavDataset, datastateActionFetchedDataentries } from "src/services/state/dataState/actions";
 
 import { getStringIdsFromRegion, getRegionHemisphere, getIdFromFullId } from 'common/util'
 import { viewerStateSelectedTemplateSelector, viewerStateSelectorNavigation } from "src/services/state/viewerState/selectors";
+import { BACKENDURL, getFetchOption, getHttpHeader } from "src/util/constants";
+import { IKgDataEntry } from ".";
 
+const SPATIAL_WIDTH = 600 
 const noMethodDisplayName = 'No methods described'
 
 /**
@@ -52,22 +53,15 @@ function generateToken() {
 export class DatabrowserService implements OnDestroy {
 
   public kgTos$: Observable<any>
-  public favedDataentries$: Observable<Partial<IDataEntry>[]>
+  public favedDataentries$: Observable<Partial<IKgDataEntry>[]>
   public darktheme: boolean = false
 
   public instantiatedWidgetUnits: WidgetUnit[] = []
-  public queryData: (arg: {regions: any[], template: any, parcellation: any}) => void = (arg) => {
-    const { widgetUnit } = this.createDatabrowser(arg)
-    this.instantiatedWidgetUnits.push(widgetUnit.instance)
-    widgetUnit.onDestroy(() => {
-      this.instantiatedWidgetUnits = this.instantiatedWidgetUnits.filter(db => db !== widgetUnit.instance)
-    })
-  }
-  public createDatabrowser: (arg: {regions: any[], template: any, parcellation: any}) => {dataBrowser: ComponentRef<DataBrowser>, widgetUnit: ComponentRef<WidgetUnit>}
-  public getDataByRegion: (arg: {regions: any[] }) => Observable<IDataEntry[]> = ({ regions }) => 
+
+  public getDataByRegion: (arg: {regions: any[] }) => Observable<IKgDataEntry[]> = ({ regions }) => 
     forkJoin(regions.map(this.getDatasetsByRegion.bind(this))).pipe(
       map(
-        (arrOfArr: IDataEntry[][]) => arrOfArr.reduce(
+        (arrOfArr: IKgDataEntry[][]) => arrOfArr.reduce(
           (acc, curr) => {
             /**
              * In the event of multi region selection
@@ -84,25 +78,25 @@ export class DatabrowserService implements OnDestroy {
     )
 
   private filterDEByRegion: FilterDataEntriesByRegion = new FilterDataEntriesByRegion()
-  private dataentries: IDataEntry[] = []
+  private dataentries: IKgDataEntry[] = []
 
   private subscriptions: Subscription[] = []
   public manualFetchDataset$: BehaviorSubject<null> = new BehaviorSubject(null)
 
-  public spatialDatasets$: Observable<any>
+  private spatialDatasets$: Observable<any>
   public viewportBoundingBox$: Observable<[Point, Point]>
 
   private templateSelected: any
 
   constructor(
     private workerService: AtlasWorkerService,
-    private constantService: AtlasViewerConstantsServices,
-    private store: Store<IavRootStoreInterface>,
+    
+    private store: Store<any>,
     private http: HttpClient,
     private log: LoggingService,
   ) {
 
-    this.kgTos$ = this.http.get(`${this.constantService.backendUrl}datasets/tos`, {
+    this.kgTos$ = this.http.get(`${BACKENDURL}datasets/tos`, {
       responseType: 'text',
     }).pipe(
       catchError((err, _obs) => {
@@ -120,9 +114,7 @@ export class DatabrowserService implements OnDestroy {
 
     this.subscriptions.push(
       store.pipe(
-        select('dataStore'),
-        safeFilter('fetchedDataEntries'),
-        map(v => v.fetchedDataEntries),
+        select('fetchedDataEntries'),
       ).subscribe(de => {
         this.dataentries = de
       }),
@@ -145,7 +137,7 @@ export class DatabrowserService implements OnDestroy {
 
         // in mm
         const center = position.map(n => n / 1e6)
-        const searchWidth = this.constantService.spatialWidth / 4 * zoom / 1e6
+        const searchWidth = SPATIAL_WIDTH / 4 * zoom / 1e6
         const pt1 = center.map(v => (v - searchWidth)) as [number, number, number]
         const pt2 = center.map(v => (v + searchWidth)) as [number, number, number]
 
@@ -167,18 +159,11 @@ export class DatabrowserService implements OnDestroy {
          */
         if (!templateSelected || !templateSelected.name) { return from(Promise.reject('templateSelected must not be empty')) }
         const encodedTemplateName = encodeURIComponent(templateSelected.name)
-        return this.http.get(`${this.constantService.backendUrl}datasets/spatialSearch/templateName/${encodedTemplateName}/bbox/${_bbox[0].join('_')}__${_bbox[1].join("_")}`).pipe(
-          catchError((err) => (this.log.log(err), of([]))),
-        )
-      }),
-    )
-
-    this.subscriptions.push(
-      this.spatialDatasets$.subscribe(arr => {
-        this.store.dispatch({
-          type: FETCHED_SPATIAL_DATA,
-          fetchedDataEntries: arr,
-        })
+        // spatial dataset has halted for the time being
+        return of(null)
+        // return this.http.get(`${BACKENDURL}datasets/spatialSearch/templateName/${encodedTemplateName}/bbox/${_bbox[0].join('_')}__${_bbox[1].join("_")}`).pipe(
+        //   catchError((err) => (this.log.log(err), of([]))),
+        // )
       }),
     )
 
@@ -203,7 +188,7 @@ export class DatabrowserService implements OnDestroy {
     this.subscriptions.forEach(s => s.unsubscribe())
   }
 
-  public toggleFav(dataentry: Partial<IDataEntry>) {
+  public toggleFav(dataentry: Partial<IKgDataEntry>) {
     this.store.dispatch(
       datastateActionToggleFav({
         payload: {
@@ -213,7 +198,7 @@ export class DatabrowserService implements OnDestroy {
     )
   }
 
-  public saveToFav(dataentry: Partial<IDataEntry>) {
+  public saveToFav(dataentry: Partial<IKgDataEntry>) {
     this.store.dispatch(
       datastateActionFavDataset({
         payload: {
@@ -223,7 +208,7 @@ export class DatabrowserService implements OnDestroy {
     )
   }
 
-  public removeFromFav(dataentry: Partial<IDataEntry>) {
+  public removeFromFav(dataentry: Partial<IKgDataEntry>) {
     this.store.dispatch(
       datastateActionUnfavDataset({
         payload: {
@@ -237,18 +222,19 @@ export class DatabrowserService implements OnDestroy {
   public fetchPreviewData(datasetName: string) {
     const encodedDatasetName = encodeURIComponent(datasetName)
     return new Promise((resolve, reject) => {
-      fetch(`${this.constantService.backendUrl}datasets/preview/${encodedDatasetName}`, this.constantService.getFetchOption())
+      fetch(`${BACKENDURL}datasets/preview/${encodedDatasetName}`, getFetchOption())
         .then(res => res.json())
         .then(resolve)
         .catch(reject)
     })
   }
 
-  private dispatchData(arr: IDataEntry[]) {
-    this.store.dispatch({
-      type : FETCHED_DATAENTRIES,
-      fetchedDataEntries : arr,
-    })
+  private dispatchData(arr: IKgDataEntry[]) {
+    this.store.dispatch(
+      datastateActionFetchedDataentries({
+        fetchedDataEntries: arr
+      })
+    )
   }
 
   public fetchedFlag: boolean = false
@@ -256,7 +242,7 @@ export class DatabrowserService implements OnDestroy {
   public fetchingFlag: boolean = false
   private mostRecentFetchToken: any
 
-  private memoizedDatasetByRegion = new Map<string, Observable<IDataEntry[]>>()
+  private memoizedDatasetByRegion = new Map<string, Observable<IKgDataEntry[]>>()
   private getDatasetsByRegion(region: { fullId: any }){
 
     const hemisphereObj = (() => {
@@ -271,14 +257,14 @@ export class DatabrowserService implements OnDestroy {
 
     for (const fullId of fullIds) {
       if (!this.memoizedDatasetByRegion.has(fullId)) {
-        const obs$ =  this.http.get<IDataEntry[]>(
-          `${this.constantService.backendUrl}datasets/byRegion/${encodeURIComponent(fullId)}`,
+        const obs$ =  this.http.get<IKgDataEntry[]>(
+          `${BACKENDURL}datasets/byRegion/${encodeURIComponent(fullId)}`,
           {
             params: {
               ...hemisphereObj,
               ...refSpaceObj
             },
-            headers: this.constantService.getHttpHeader(),
+            headers: getHttpHeader(),
             responseType: 'json'
           }
         ).pipe(
@@ -299,14 +285,14 @@ export class DatabrowserService implements OnDestroy {
     )
   }
 
-  private lowLevelQuery(templateName: string, parcellationName: string): Promise<IDataEntry[]> {
+  private lowLevelQuery(templateName: string, parcellationName: string): Promise<IKgDataEntry[]> {
     const encodedTemplateName = encodeURIComponent(templateName)
     const encodedParcellationName = encodeURIComponent(parcellationName)
 
     return this.http.get(
-      `${this.constantService.backendUrl}datasets//templateNameParcellationName/${encodedTemplateName}/${encodedParcellationName}`,
+      `${BACKENDURL}datasets//templateNameParcellationName/${encodedTemplateName}/${encodedParcellationName}`,
       {
-        headers: this.constantService.getHttpHeader(),
+        headers: getHttpHeader(),
         responseType: 'json'
       }
     ).pipe(
@@ -315,7 +301,7 @@ export class DatabrowserService implements OnDestroy {
           const newMap = new Map(acc)
           return newMap.set(item.name, item)
         }, new Map())
-        return Array.from(map.values() as IDataEntry[])
+        return Array.from(map.values() as IKgDataEntry[])
       })
     ).toPromise()
   }
@@ -359,16 +345,10 @@ export class DatabrowserService implements OnDestroy {
     })
   }
 
-  public dbComponentInit(_db: DataBrowser) {
-    this.store.dispatch({
-      type: SHOW_KG_TOS,
-    })
-  }
-
   public getModalityFromDE = getModalityFromDE
 }
 
-export function reduceDataentry(accumulator: Array<{name: string, occurance: number}>, dataentry: IDataEntry) {
+export function reduceDataentry(accumulator: Array<{name: string, occurance: number}>, dataentry: IKgDataEntry) {
   const methods = (dataentry.methods || [])
     .reduce((acc, item) => acc.concat(
       item.length > 0
@@ -395,11 +375,11 @@ export function reduceDataentry(accumulator: Array<{name: string, occurance: num
   }))
 }
 
-export function getModalityFromDE(dataentries: IDataEntry[]): CountedDataModality[] {
+export function getModalityFromDE(dataentries: IKgDataEntry[]): CountedDataModality[] {
   return dataentries.reduce((acc, de) => reduceDataentry(acc, de), [])
 }
 
-export function getIdFromDataEntry(dataentry: IDataEntry) {
+export function getIdFromDataEntry(dataentry: IKgDataEntry) {
   const { id, fullId } = dataentry
   const regex = /\/([a-zA-Z0-9-]*?)$/.exec(fullId)
   return (regex && regex[1]) || id
diff --git a/src/ui/databrowserModule/databrowser.useEffect.spec.ts b/src/atlasComponents/databrowserModule/databrowser.useEffect.spec.ts
similarity index 100%
rename from src/ui/databrowserModule/databrowser.useEffect.spec.ts
rename to src/atlasComponents/databrowserModule/databrowser.useEffect.spec.ts
diff --git a/src/ui/databrowserModule/databrowser.useEffect.ts b/src/atlasComponents/databrowserModule/databrowser.useEffect.ts
similarity index 100%
rename from src/ui/databrowserModule/databrowser.useEffect.ts
rename to src/atlasComponents/databrowserModule/databrowser.useEffect.ts
diff --git a/src/ui/databrowserModule/index.ts b/src/atlasComponents/databrowserModule/index.ts
similarity index 100%
rename from src/ui/databrowserModule/index.ts
rename to src/atlasComponents/databrowserModule/index.ts
diff --git a/src/ui/databrowserModule/kgSingleDatasetService.service.ts b/src/atlasComponents/databrowserModule/kgSingleDatasetService.service.ts
similarity index 84%
rename from src/ui/databrowserModule/kgSingleDatasetService.service.ts
rename to src/atlasComponents/databrowserModule/kgSingleDatasetService.service.ts
index d182542fa3fe6593809aabb14f2b0872036014a7..e25ad5e2c1e706def9c82fed040e6644f1047ce9 100644
--- a/src/ui/databrowserModule/kgSingleDatasetService.service.ts
+++ b/src/atlasComponents/databrowserModule/kgSingleDatasetService.service.ts
@@ -3,8 +3,6 @@ import { Injectable, OnDestroy, TemplateRef } from "@angular/core";
 import { select, Store } from "@ngrx/store";
 import { Observable, Subscription } from "rxjs";
 import { filter, shareReplay } from "rxjs/operators";
-import { IDataEntry, ViewerPreviewFile, DATASETS_ACTIONS_TYPES } from "src/services/state/dataStore.store";
-import { IavRootStoreInterface } from "src/services/stateStore.service";
 import { BACKENDURL } from "src/util/constants";
 import { uiStateShowBottomSheet } from "src/services/state/uiState.store.helper";
 import { ngViewerActionRemoveNgLayer } from "src/services/state/ngViewerState/actions";
@@ -16,7 +14,7 @@ export class KgSingleDatasetService implements OnDestroy {
   public ngLayers: Set<string> = new Set()
 
   constructor(
-    private store$: Store<IavRootStoreInterface>,
+    private store$: Store<any>,
     private http: HttpClient,
   ) {
 
@@ -71,16 +69,6 @@ export class KgSingleDatasetService implements OnDestroy {
     )
   }
 
-  public previewFile(file: Partial<ViewerPreviewFile>, dataset: Partial<IDataEntry>) {
-    this.store$.dispatch({
-      type: DATASETS_ACTIONS_TYPES.PREVIEW_DATASET,
-      payload: {
-        file,
-        dataset
-      }
-    })
-  }
-
   public removeNgLayer({ url }) {
     this.store$.dispatch(
       ngViewerActionRemoveNgLayer({
diff --git a/src/ui/databrowserModule/preview/datasetPreviews/datasetPreviewsList/datasetPreviewList.component.ts b/src/atlasComponents/databrowserModule/preview/datasetPreviews/datasetPreviewsList/datasetPreviewList.component.ts
similarity index 100%
rename from src/ui/databrowserModule/preview/datasetPreviews/datasetPreviewsList/datasetPreviewList.component.ts
rename to src/atlasComponents/databrowserModule/preview/datasetPreviews/datasetPreviewsList/datasetPreviewList.component.ts
diff --git a/src/ui/databrowserModule/preview/datasetPreviews/datasetPreviewsList/datasetPreviewList.template.html b/src/atlasComponents/databrowserModule/preview/datasetPreviews/datasetPreviewsList/datasetPreviewList.template.html
similarity index 100%
rename from src/ui/databrowserModule/preview/datasetPreviews/datasetPreviewsList/datasetPreviewList.template.html
rename to src/atlasComponents/databrowserModule/preview/datasetPreviews/datasetPreviewsList/datasetPreviewList.template.html
diff --git a/src/ui/databrowserModule/preview/preview.base.ts b/src/atlasComponents/databrowserModule/preview/preview.base.ts
similarity index 100%
rename from src/ui/databrowserModule/preview/preview.base.ts
rename to src/atlasComponents/databrowserModule/preview/preview.base.ts
diff --git a/src/ui/databrowserModule/preview/previewComponentWrapper/previewCW.component.ts b/src/atlasComponents/databrowserModule/preview/previewComponentWrapper/previewCW.component.ts
similarity index 100%
rename from src/ui/databrowserModule/preview/previewComponentWrapper/previewCW.component.ts
rename to src/atlasComponents/databrowserModule/preview/previewComponentWrapper/previewCW.component.ts
diff --git a/src/ui/databrowserModule/preview/previewComponentWrapper/previewCW.style.css b/src/atlasComponents/databrowserModule/preview/previewComponentWrapper/previewCW.style.css
similarity index 100%
rename from src/ui/databrowserModule/preview/previewComponentWrapper/previewCW.style.css
rename to src/atlasComponents/databrowserModule/preview/previewComponentWrapper/previewCW.style.css
diff --git a/src/ui/databrowserModule/preview/previewComponentWrapper/previewCW.template.html b/src/atlasComponents/databrowserModule/preview/previewComponentWrapper/previewCW.template.html
similarity index 100%
rename from src/ui/databrowserModule/preview/previewComponentWrapper/previewCW.template.html
rename to src/atlasComponents/databrowserModule/preview/previewComponentWrapper/previewCW.template.html
diff --git a/src/ui/databrowserModule/preview/previewDatasetFile.directive.ts b/src/atlasComponents/databrowserModule/preview/previewDatasetFile.directive.ts
similarity index 100%
rename from src/ui/databrowserModule/preview/previewDatasetFile.directive.ts
rename to src/atlasComponents/databrowserModule/preview/previewDatasetFile.directive.ts
diff --git a/src/ui/databrowserModule/preview/previewFileIcon.pipe.ts b/src/atlasComponents/databrowserModule/preview/previewFileIcon.pipe.ts
similarity index 100%
rename from src/ui/databrowserModule/preview/previewFileIcon.pipe.ts
rename to src/atlasComponents/databrowserModule/preview/previewFileIcon.pipe.ts
diff --git a/src/ui/databrowserModule/preview/previewFileType.pipe.ts b/src/atlasComponents/databrowserModule/preview/previewFileType.pipe.ts
similarity index 100%
rename from src/ui/databrowserModule/preview/previewFileType.pipe.ts
rename to src/atlasComponents/databrowserModule/preview/previewFileType.pipe.ts
diff --git a/src/ui/databrowserModule/preview/shownPreviews.directive.ts b/src/atlasComponents/databrowserModule/preview/shownPreviews.directive.ts
similarity index 100%
rename from src/ui/databrowserModule/preview/shownPreviews.directive.ts
rename to src/atlasComponents/databrowserModule/preview/shownPreviews.directive.ts
diff --git a/src/ui/databrowserModule/pure.spec.ts b/src/atlasComponents/databrowserModule/pure.spec.ts
similarity index 61%
rename from src/ui/databrowserModule/pure.spec.ts
rename to src/atlasComponents/databrowserModule/pure.spec.ts
index 30d3bbf8e308180db32d061fbce6fdc81796a0f8..6527e6a3ef4e305c7426060a87f0f6eb9ede5f4b 100644
--- a/src/ui/databrowserModule/pure.spec.ts
+++ b/src/atlasComponents/databrowserModule/pure.spec.ts
@@ -1,7 +1,7 @@
-import * as _ from './pure'
+// import * as _ from './pure'
 
 describe('> pure.ts', () => {
   it('> should be importable without hiccups', () => {
-    console.log(Object.keys(_))
+    
   })
 })
diff --git a/src/ui/databrowserModule/pure.ts b/src/atlasComponents/databrowserModule/pure.ts
similarity index 100%
rename from src/ui/databrowserModule/pure.ts
rename to src/atlasComponents/databrowserModule/pure.ts
diff --git a/src/ui/databrowserModule/singleDataset/listView/singleDatasetListView.component.ts b/src/atlasComponents/databrowserModule/singleDataset/listView/singleDatasetListView.component.ts
similarity index 100%
rename from src/ui/databrowserModule/singleDataset/listView/singleDatasetListView.component.ts
rename to src/atlasComponents/databrowserModule/singleDataset/listView/singleDatasetListView.component.ts
diff --git a/src/ui/databrowserModule/singleDataset/listView/singleDatasetListView.style.css b/src/atlasComponents/databrowserModule/singleDataset/listView/singleDatasetListView.style.css
similarity index 100%
rename from src/ui/databrowserModule/singleDataset/listView/singleDatasetListView.style.css
rename to src/atlasComponents/databrowserModule/singleDataset/listView/singleDatasetListView.style.css
diff --git a/src/ui/databrowserModule/singleDataset/listView/singleDatasetListView.template.html b/src/atlasComponents/databrowserModule/singleDataset/listView/singleDatasetListView.template.html
similarity index 100%
rename from src/ui/databrowserModule/singleDataset/listView/singleDatasetListView.template.html
rename to src/atlasComponents/databrowserModule/singleDataset/listView/singleDatasetListView.template.html
diff --git a/src/ui/databrowserModule/singleDataset/singleDataset.base.spec.ts b/src/atlasComponents/databrowserModule/singleDataset/singleDataset.base.spec.ts
similarity index 96%
rename from src/ui/databrowserModule/singleDataset/singleDataset.base.spec.ts
rename to src/atlasComponents/databrowserModule/singleDataset/singleDataset.base.spec.ts
index 8389e1c832e42267bb8adf29de8084561fa6f3a5..9636fe82b78960f4396cd1ba7df905e0c5b8633e 100644
--- a/src/ui/databrowserModule/singleDataset/singleDataset.base.spec.ts
+++ b/src/atlasComponents/databrowserModule/singleDataset/singleDataset.base.spec.ts
@@ -1,4 +1,3 @@
-import { SingleDatasetView } from './detailedView/singleDataset.component'
 import { TestBed, async } from '@angular/core/testing';
 import { AngularMaterialModule } from 'src/ui/sharedModules/angularMaterial.module';
 import { ComponentsModule } from 'src/components/components.module';
diff --git a/src/ui/databrowserModule/singleDataset/singleDataset.base.ts b/src/atlasComponents/databrowserModule/singleDataset/singleDataset.base.ts
similarity index 100%
rename from src/ui/databrowserModule/singleDataset/singleDataset.base.ts
rename to src/atlasComponents/databrowserModule/singleDataset/singleDataset.base.ts
diff --git a/src/ui/databrowserModule/singleDataset/singleDataset.directive.ts b/src/atlasComponents/databrowserModule/singleDataset/singleDataset.directive.ts
similarity index 100%
rename from src/ui/databrowserModule/singleDataset/singleDataset.directive.ts
rename to src/atlasComponents/databrowserModule/singleDataset/singleDataset.directive.ts
diff --git a/src/ui/databrowserModule/store.module.ts b/src/atlasComponents/databrowserModule/store.module.ts
similarity index 100%
rename from src/ui/databrowserModule/store.module.ts
rename to src/atlasComponents/databrowserModule/store.module.ts
diff --git a/src/ui/databrowserModule/util/aggregateArrayIntoRoot.pipe.ts b/src/atlasComponents/databrowserModule/util/aggregateArrayIntoRoot.pipe.ts
similarity index 100%
rename from src/ui/databrowserModule/util/aggregateArrayIntoRoot.pipe.ts
rename to src/atlasComponents/databrowserModule/util/aggregateArrayIntoRoot.pipe.ts
diff --git a/src/ui/databrowserModule/util/copyProperty.pipe.spec.ts b/src/atlasComponents/databrowserModule/util/copyProperty.pipe.spec.ts
similarity index 100%
rename from src/ui/databrowserModule/util/copyProperty.pipe.spec.ts
rename to src/atlasComponents/databrowserModule/util/copyProperty.pipe.spec.ts
diff --git a/src/ui/databrowserModule/util/copyProperty.pipe.ts b/src/atlasComponents/databrowserModule/util/copyProperty.pipe.ts
similarity index 100%
rename from src/ui/databrowserModule/util/copyProperty.pipe.ts
rename to src/atlasComponents/databrowserModule/util/copyProperty.pipe.ts
diff --git a/src/ui/databrowserModule/util/datasetIsFaved.pipe.ts b/src/atlasComponents/databrowserModule/util/datasetIsFaved.pipe.ts
similarity index 100%
rename from src/ui/databrowserModule/util/datasetIsFaved.pipe.ts
rename to src/atlasComponents/databrowserModule/util/datasetIsFaved.pipe.ts
diff --git a/src/ui/databrowserModule/util/filterDataEntriesByMethods.pipe.ts b/src/atlasComponents/databrowserModule/util/filterDataEntriesByMethods.pipe.ts
similarity index 100%
rename from src/ui/databrowserModule/util/filterDataEntriesByMethods.pipe.ts
rename to src/atlasComponents/databrowserModule/util/filterDataEntriesByMethods.pipe.ts
diff --git a/src/ui/databrowserModule/util/filterDataEntriesByRegion.pipe.spec.ts b/src/atlasComponents/databrowserModule/util/filterDataEntriesByRegion.pipe.spec.ts
similarity index 100%
rename from src/ui/databrowserModule/util/filterDataEntriesByRegion.pipe.spec.ts
rename to src/atlasComponents/databrowserModule/util/filterDataEntriesByRegion.pipe.spec.ts
diff --git a/src/ui/databrowserModule/util/filterDataEntriesByRegion.pipe.ts b/src/atlasComponents/databrowserModule/util/filterDataEntriesByRegion.pipe.ts
similarity index 91%
rename from src/ui/databrowserModule/util/filterDataEntriesByRegion.pipe.ts
rename to src/atlasComponents/databrowserModule/util/filterDataEntriesByRegion.pipe.ts
index cea1159819a66e785405a6eb0fb40445f7a8edf4..8995507c1841077639afd22f4f3bfcea41d7ed3d 100644
--- a/src/ui/databrowserModule/util/filterDataEntriesByRegion.pipe.ts
+++ b/src/atlasComponents/databrowserModule/util/filterDataEntriesByRegion.pipe.ts
@@ -20,7 +20,6 @@ const isSubRegion = (high, low) => regionsEqual(high, low)
     
 
 const filterSubSelect = (dataEntry, selectedRegions) => {
-  if (dataEntry.name === 'Density measurements of different receptors for Area 7A (SPL) [human, v1.0]') console.log(dataEntry)
   return dataEntry.parcellationRegion.some(pr => selectedRegions.some(sr => isSubRegion(pr, sr)))
 }
 
diff --git a/src/ui/databrowserModule/util/getKgSchemaIdFromFullId.pipe.ts b/src/atlasComponents/databrowserModule/util/getKgSchemaIdFromFullId.pipe.ts
similarity index 100%
rename from src/ui/databrowserModule/util/getKgSchemaIdFromFullId.pipe.ts
rename to src/atlasComponents/databrowserModule/util/getKgSchemaIdFromFullId.pipe.ts
diff --git a/src/ui/databrowserModule/util/pathToNestedChildren.pipe.spec.ts b/src/atlasComponents/databrowserModule/util/pathToNestedChildren.pipe.spec.ts
similarity index 100%
rename from src/ui/databrowserModule/util/pathToNestedChildren.pipe.spec.ts
rename to src/atlasComponents/databrowserModule/util/pathToNestedChildren.pipe.spec.ts
diff --git a/src/ui/databrowserModule/util/pathToNestedChildren.pipe.ts b/src/atlasComponents/databrowserModule/util/pathToNestedChildren.pipe.ts
similarity index 100%
rename from src/ui/databrowserModule/util/pathToNestedChildren.pipe.ts
rename to src/atlasComponents/databrowserModule/util/pathToNestedChildren.pipe.ts
diff --git a/src/ui/databrowserModule/util/previewFileDisabledByReferenceSpace.pipe.ts b/src/atlasComponents/databrowserModule/util/previewFileDisabledByReferenceSpace.pipe.ts
similarity index 100%
rename from src/ui/databrowserModule/util/previewFileDisabledByReferenceSpace.pipe.ts
rename to src/atlasComponents/databrowserModule/util/previewFileDisabledByReferenceSpace.pipe.ts
diff --git a/src/ui/databrowserModule/util/regionBackgroundToRgb.pipe.ts b/src/atlasComponents/databrowserModule/util/regionBackgroundToRgb.pipe.ts
similarity index 100%
rename from src/ui/databrowserModule/util/regionBackgroundToRgb.pipe.ts
rename to src/atlasComponents/databrowserModule/util/regionBackgroundToRgb.pipe.ts
diff --git a/src/atlasComponents/parcellation/index.ts b/src/atlasComponents/parcellation/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4af8252e223c89fce580454087e7038563e1fab4
--- /dev/null
+++ b/src/atlasComponents/parcellation/index.ts
@@ -0,0 +1,4 @@
+export { FilterNameBySearch } from "./regionHierachy/filterNameBySearch.pipe";
+export { AtlasCmpParcellationModule } from "./module";
+export { RegionHierarchy } from "./regionHierachy/regionHierarchy.component";
+export { RegionTextSearchAutocomplete } from "./regionSearch/regionSearch.component";
diff --git a/src/atlasComponents/parcellation/module.ts b/src/atlasComponents/parcellation/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..78821146430138aa7afe3998ce3959c09f3c099f
--- /dev/null
+++ b/src/atlasComponents/parcellation/module.ts
@@ -0,0 +1,34 @@
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module";
+import { ParcellationRegionModule } from "src/atlasComponents/parcellationRegion";
+import { RegionHierarchy } from "./regionHierachy/regionHierarchy.component";
+import { RegionTextSearchAutocomplete } from "./regionSearch/regionSearch.component";
+import { FilterNameBySearch } from "./regionHierachy/filterNameBySearch.pipe";
+import { UtilModule } from "src/util";
+import { FormsModule, ReactiveFormsModule } from "@angular/forms";
+import { ComponentsModule } from "src/components";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    UtilModule,
+    FormsModule,
+    ReactiveFormsModule,
+    AngularMaterialModule,
+    ParcellationRegionModule,
+    ComponentsModule,
+  ],
+  declarations: [
+    RegionHierarchy,
+    RegionTextSearchAutocomplete,
+
+    FilterNameBySearch,
+  ],
+  exports: [
+    RegionHierarchy,
+    RegionTextSearchAutocomplete,
+    FilterNameBySearch,
+  ]
+})
+export class AtlasCmpParcellationModule{}
\ No newline at end of file
diff --git a/src/ui/viewerStateController/regionHierachy/filterNameBySearch.pipe.ts b/src/atlasComponents/parcellation/regionHierachy/filterNameBySearch.pipe.ts
similarity index 100%
rename from src/ui/viewerStateController/regionHierachy/filterNameBySearch.pipe.ts
rename to src/atlasComponents/parcellation/regionHierachy/filterNameBySearch.pipe.ts
diff --git a/src/ui/viewerStateController/regionHierachy/regionHierarchy.component.ts b/src/atlasComponents/parcellation/regionHierachy/regionHierarchy.component.ts
similarity index 97%
rename from src/ui/viewerStateController/regionHierachy/regionHierarchy.component.ts
rename to src/atlasComponents/parcellation/regionHierachy/regionHierarchy.component.ts
index 2534f5548129666f3201ea821e1319c6b2bbc572..715f000757552e41ee4e67ace06e9de56006e69e 100644
--- a/src/ui/viewerStateController/regionHierachy/regionHierarchy.component.ts
+++ b/src/atlasComponents/parcellation/regionHierachy/regionHierarchy.component.ts
@@ -1,8 +1,8 @@
 import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, HostListener, Input, OnInit, Output, ViewChild } from "@angular/core";
 import { fromEvent, Subject, Subscription } from "rxjs";
 import { buffer, debounceTime } from "rxjs/operators";
-import { generateLabelIndexId } from "src/services/stateStore.service";
 import { FilterNameBySearch } from "./filterNameBySearch.pipe";
+import { serialiseParcellationRegion } from "common/util"
 
 const insertHighlight: (name: string, searchTerm: string) => string = (name: string, searchTerm: string = '') => {
   const regex = new RegExp(searchTerm, 'gi')
@@ -15,7 +15,7 @@ const getDisplayTreeNode: (searchTerm: string, selectedRegions: any[]) => (item:
   return !!labelIndex
     && !!ngId
     && selectedRegions.findIndex(re =>
-      generateLabelIndexId({ labelIndex: re.labelIndex, ngId: re.ngId }) === generateLabelIndexId({ ngId, labelIndex }),
+      serialiseParcellationRegion({ labelIndex: re.labelIndex, ngId: re.ngId }) === serialiseParcellationRegion({ ngId, labelIndex }),
     ) >= 0
     ? `<span class="cursor-default regionSelected">${insertHighlight(name, searchTerm)}</span>` + (status ? ` <span class="text-muted">(${insertHighlight(status, searchTerm)})</span>` : ``)
     : `<span class="cursor-default regionNotSelected">${insertHighlight(name, searchTerm)}</span>` + (status ? ` <span class="text-muted">(${insertHighlight(status, searchTerm)})</span>` : ``)
diff --git a/src/ui/viewerStateController/regionHierachy/regionHierarchy.style.css b/src/atlasComponents/parcellation/regionHierachy/regionHierarchy.style.css
similarity index 100%
rename from src/ui/viewerStateController/regionHierachy/regionHierarchy.style.css
rename to src/atlasComponents/parcellation/regionHierachy/regionHierarchy.style.css
diff --git a/src/ui/viewerStateController/regionHierachy/regionHierarchy.template.html b/src/atlasComponents/parcellation/regionHierachy/regionHierarchy.template.html
similarity index 100%
rename from src/ui/viewerStateController/regionHierachy/regionHierarchy.template.html
rename to src/atlasComponents/parcellation/regionHierachy/regionHierarchy.template.html
diff --git a/src/ui/viewerStateController/regionSearch/regionSearch.component.ts b/src/atlasComponents/parcellation/regionSearch/regionSearch.component.ts
similarity index 91%
rename from src/ui/viewerStateController/regionSearch/regionSearch.component.ts
rename to src/atlasComponents/parcellation/regionSearch/regionSearch.component.ts
index c5989bbb4f29ac46ba5ce1d05fc87974f95c95da..b3db031c4416658fed8c1de4d7936607f3f5e74e 100644
--- a/src/ui/viewerStateController/regionSearch/regionSearch.component.ts
+++ b/src/atlasComponents/parcellation/regionSearch/regionSearch.component.ts
@@ -4,14 +4,16 @@ import { select, Store } from "@ngrx/store";
 import { combineLatest, Observable, Subject, merge } from "rxjs";
 import { debounceTime, distinctUntilChanged, filter, map, shareReplay, startWith, take, tap, withLatestFrom } from "rxjs/operators";
 import { VIEWER_STATE_ACTION_TYPES } from "src/services/effect/effect";
-import { ADD_TO_REGIONS_SELECTION_WITH_IDS, CHANGE_NAVIGATION, SELECT_REGIONS } from "src/services/state/viewerState.store";
-import { generateLabelIndexId, getMultiNgIdsRegionsLabelIndexMap, IavRootStoreInterface } from "src/services/stateStore.service";
+import { CHANGE_NAVIGATION, SELECT_REGIONS } from "src/services/state/viewerState.store";
+import { getMultiNgIdsRegionsLabelIndexMap } from "src/services/stateStore.service";
 import { LoggingService } from "src/logging";
 import { MatDialog } from "@angular/material/dialog";
 import { MatAutocompleteSelectedEvent } from "@angular/material/autocomplete";
 import { PureContantService } from "src/util";
 import { viewerStateToggleRegionSelect, viewerStateNavigateToRegion, viewerStateSetSelectedRegions, viewerStateSetSelectedRegionsWithIds } from "src/services/state/viewerState.store.helper";
 import { ARIA_LABELS, CONST } from 'common/constants'
+import { serialiseParcellationRegion } from "common/util"
+import { actionAddToRegionsSelectionWithIds } from "src/services/state/viewerState/actions";
 
 const filterRegionBasedOnText = searchTerm => region => `${region.name.toLowerCase()}${region.status? ' (' + region.status + ')' : null}`.includes(searchTerm.toLowerCase())
   || (region.relatedAreas && region.relatedAreas.some(relatedArea => relatedArea.name && relatedArea.name.toLowerCase().includes(searchTerm.toLowerCase())))
@@ -53,7 +55,7 @@ export class RegionTextSearchAutocomplete {
   public selectedRegionLabelIndexSet: Set<string> = new Set()
 
   constructor(
-    private store$: Store<IavRootStoreInterface>,
+    private store$: Store<any>,
     private dialog: MatDialog,
     private pureConstantService: PureContantService,
     private log: LoggingService
@@ -80,7 +82,7 @@ export class RegionTextSearchAutocomplete {
                 ...region,
                 ngId,
                 labelIndex,
-                labelIndexId: generateLabelIndexId({ ngId, labelIndex }),
+                labelIndexId: serialiseParcellationRegion({ ngId, labelIndex }),
               })
             }
           }
@@ -97,7 +99,7 @@ export class RegionTextSearchAutocomplete {
       select('regionsSelected'),
       distinctUntilChanged(),
       tap(regions => {
-        const arrLabelIndexId = regions.map(({ ngId, labelIndex }) => generateLabelIndexId({ ngId, labelIndex }))
+        const arrLabelIndexId = regions.map(({ ngId, labelIndex }) => serialiseParcellationRegion({ ngId, labelIndex }))
         this.selectedRegionLabelIndexSet = new Set(arrLabelIndexId)
       }),
       startWith([]),
@@ -139,10 +141,11 @@ export class RegionTextSearchAutocomplete {
         deselecRegionIds: [id],
       })
     } else {
-      this.store$.dispatch({
-        type: ADD_TO_REGIONS_SELECTION_WITH_IDS,
-        selectRegionIds : [id],
-      })
+      this.store$.dispatch(
+        actionAddToRegionsSelectionWithIds({
+          selectRegionIds : [id],
+        })
+      )
     }
   }
 
diff --git a/src/ui/viewerStateController/regionSearch/regionSearch.style.css b/src/atlasComponents/parcellation/regionSearch/regionSearch.style.css
similarity index 100%
rename from src/ui/viewerStateController/regionSearch/regionSearch.style.css
rename to src/atlasComponents/parcellation/regionSearch/regionSearch.style.css
diff --git a/src/ui/viewerStateController/regionSearch/regionSearch.template.html b/src/atlasComponents/parcellation/regionSearch/regionSearch.template.html
similarity index 100%
rename from src/ui/viewerStateController/regionSearch/regionSearch.template.html
rename to src/atlasComponents/parcellation/regionSearch/regionSearch.template.html
diff --git a/src/atlasComponents/parcellationRegion/index.ts b/src/atlasComponents/parcellationRegion/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b3f4198bc8c372d02e1c43b01cd9809d18de3d1c
--- /dev/null
+++ b/src/atlasComponents/parcellationRegion/index.ts
@@ -0,0 +1,10 @@
+export {
+  ParcellationRegionModule,
+} from "./module"
+
+
+export { RegionDirective } from "./region.directive";
+export { RegionListSimpleViewComponent } from "./regionListSimpleView/regionListSimpleView.component";
+export { RegionMenuComponent } from "./regionMenu/regionMenu.component";
+export { SimpleRegionComponent } from "./regionSimple/regionSimple.component";
+export { RenderViewOriginDatasetLabelPipe } from "./region.base";
\ No newline at end of file
diff --git a/src/atlasComponents/parcellationRegion/module.ts b/src/atlasComponents/parcellationRegion/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ea4826233f38bc1510ba27efe42329297dc9ba2a
--- /dev/null
+++ b/src/atlasComponents/parcellationRegion/module.ts
@@ -0,0 +1,45 @@
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { ComponentsModule } from "src/components";
+import { DatabrowserModule } from "src/atlasComponents/databrowserModule";
+import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module";
+import { UtilModule } from "src/util";
+import { RenderViewOriginDatasetLabelPipe } from "./region.base";
+import { RegionDirective } from "./region.directive";
+import { RegionListSimpleViewComponent } from "./regionListSimpleView/regionListSimpleView.component";
+import { RegionMenuComponent } from "./regionMenu/regionMenu.component";
+import { SimpleRegionComponent } from "./regionSimple/regionSimple.component";
+import { BSFeatureModule } from "../regionalFeatures/bsFeatures";
+import { RegionAccordionTooltipTextPipe } from "./regionAccordionTooltipText.pipe";
+import { AtlasCmptConnModule } from "../connectivity";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    UtilModule,
+    DatabrowserModule,
+    AngularMaterialModule,
+    ComponentsModule,
+    BSFeatureModule,
+    AtlasCmptConnModule,
+  ],
+  declarations: [
+    RegionMenuComponent,
+    RegionListSimpleViewComponent,
+    SimpleRegionComponent,
+
+    RegionDirective,
+    RenderViewOriginDatasetLabelPipe,
+    RegionAccordionTooltipTextPipe,
+  ],
+  exports: [
+    RegionMenuComponent,
+    RegionListSimpleViewComponent,
+    SimpleRegionComponent,
+
+    RegionDirective,
+    RenderViewOriginDatasetLabelPipe,
+  ]
+})
+
+export class ParcellationRegionModule{}
\ No newline at end of file
diff --git a/src/ui/parcellationRegion/region.base.spec.ts b/src/atlasComponents/parcellationRegion/region.base.spec.ts
similarity index 57%
rename from src/ui/parcellationRegion/region.base.spec.ts
rename to src/atlasComponents/parcellationRegion/region.base.spec.ts
index c2fba7c31109839e617538b27f924a29aa5bd6f3..c9ee2964842a9b2b61c37d2e257461157f0bde7e 100644
--- a/src/ui/parcellationRegion/region.base.spec.ts
+++ b/src/atlasComponents/parcellationRegion/region.base.spec.ts
@@ -1,7 +1,8 @@
 import { TestBed } from '@angular/core/testing'
 import { MockStore, provideMockStore } from '@ngrx/store/testing'
-import { viewerStateNewViewer } from 'src/services/state/viewerState/actions'
+import { viewerStateSelectTemplateWithId } from 'src/services/state/viewerState/actions'
 import { RegionBase, regionInOtherTemplateSelector, getRegionParentParcRefSpace } from './region.base'
+const  util = require('common/util')
 
 /**
  * regions
@@ -10,7 +11,8 @@ import { RegionBase, regionInOtherTemplateSelector, getRegionParentParcRefSpace
 const mr0 = {
   labelIndex: 1,
   name: 'mr0',
-  fullId: {
+  availableIn: [{id: 'fzj/mock/rs/v0.0.0/aaa-bbb'}, {id: 'fzj/mock/rs/v0.0.0/bbb-bbb'}, {id: 'fzj/mock/rs/v0.0.0/ccc-bbb'}],
+  id: {
     kg: {
       kgSchema: 'fzj/mock/pr',
       kgId: 'aaa-bbb'
@@ -33,7 +35,7 @@ const getRegionInOtherTemplateSelectorBundle = (version: EnumParcRegVersion) =>
       const mr1wrong = {
         labelIndex: 1,
         name: 'mr1',
-        fullId: {
+        id: {
           kg: {
             kgSchema: 'fzj/mock/pr',
             kgId: 'fff-bbb'
@@ -44,7 +46,7 @@ const getRegionInOtherTemplateSelectorBundle = (version: EnumParcRegVersion) =>
       const mr0wrong = {
         labelIndex: 1,
         name: 'mr0',
-        fullId: {
+        id: {
           kg: {
             kgSchema: 'fzj/mock/pr',
             kgId: 'aaa-fff'
@@ -55,8 +57,8 @@ const getRegionInOtherTemplateSelectorBundle = (version: EnumParcRegVersion) =>
 
       const mr1lh = {
         labelIndex: 1,
-        name: 'mr1 - left hemisphere',
-        fullId: {
+        name: 'mr1 left',
+        id: {
           kg: {
             kgSchema: 'fzj/mock/pr',
             kgId: 'ccc-bbb'
@@ -66,8 +68,8 @@ const getRegionInOtherTemplateSelectorBundle = (version: EnumParcRegVersion) =>
 
       const mr1rh = {
         labelIndex: 1,
-        name: 'mr1 - right hemisphere',
-        fullId: {
+        name: 'mr1 right',
+        id: {
           kg: {
             kgSchema: 'fzj/mock/pr',
             kgId: 'ccc-bbb'
@@ -75,10 +77,16 @@ const getRegionInOtherTemplateSelectorBundle = (version: EnumParcRegVersion) =>
         }
       }
 
+      const mr0nh = {
+        labelIndex: 11,
+        name: 'mr0',
+      }
+
       const mr0lh = {
         labelIndex: 1,
-        name: 'mr0 - left hemisphere',
-        fullId: {
+        name: 'mr0 left',
+        availableIn: [{id: 'fzj/mock/rs/v0.0.0/aaa-bbb'}, {id: 'fzj/mock/rs/v0.0.0/bbb-bbb'}, {id: 'fzj/mock/rs/v0.0.0/ccc-bbb'}],
+        id: {
           kg: {
             kgSchema: 'fzj/mock/pr',
             kgId: 'aaa-bbb'
@@ -88,8 +96,8 @@ const getRegionInOtherTemplateSelectorBundle = (version: EnumParcRegVersion) =>
 
       const mr0rh = {
         labelIndex: 1,
-        name: 'mr0 - right hemisphere',
-        fullId: {
+        name: 'mr0 right',
+        id: {
           kg: {
             kgSchema: 'fzj/mock/pr',
             kgId: 'aaa-bbb'
@@ -100,7 +108,7 @@ const getRegionInOtherTemplateSelectorBundle = (version: EnumParcRegVersion) =>
       const mr1 = {
         labelIndex: 1,
         name: 'mr1',
-        fullId: {
+        id: {
           kg: {
             kgSchema: 'fzj/mock/pr',
             kgId: 'ccc-bbb'
@@ -112,16 +120,19 @@ const getRegionInOtherTemplateSelectorBundle = (version: EnumParcRegVersion) =>
 
       const mp1h = {
         name: 'mp1h',
-        regions: [ mr1lh, mr0lh,  mr0rh, mr1rh ]
+        '@id': 'parcellation/id',
+        regions: [ mr0nh, mr1lh, mr0lh,  mr0rh, mr1rh ]
       }
 
       const mpWrong = {
         name: 'mp1h',
+        '@id': 'parcellation/id',
         regions: [ mr1wrong, mr0wrong ]
       }
 
       const mp0 = {
         name: 'mp0',
+        '@id': 'parcellation/id',
         regions: [ mr1, mr0 ]
       }
 
@@ -129,31 +140,31 @@ const getRegionInOtherTemplateSelectorBundle = (version: EnumParcRegVersion) =>
 
       const mt0 = {
         name: 'mt0',
-        fullId: 'fzj/mock/rs/v0.0.0/aaa-bbb',
+        '@id': 'fzj/mock/rs/v0.0.0/aaa-bbb',
         parcellations: [ mp0 ]
       }
 
       const mt1 = {
         name: 'mt1',
-        fullId: 'fzj/mock/rs/v0.0.0/bbb-bbb',
+        '@id': 'fzj/mock/rs/v0.0.0/bbb-bbb',
         parcellations: [ mp0 ]
       }
 
       const mt2 = {
         name: 'mt2',
-        fullId: 'fzj/mock/rs/v0.0.0/ccc-bbb',
+        '@id': 'fzj/mock/rs/v0.0.0/ccc-bbb',
         parcellations: [ mp1h ]
       }
 
       const mt3 = {
         name: 'mt3',
-        fullId: 'fzj/mock/rs/v0.0.0/ddd-bbb',
+        '@id': 'fzj/mock/rs/v0.0.0/ddd-bbb',
         parcellations: [ mp1h ]
       }
 
       const mtWrong = {
         name: 'mtWrong',
-        fullId: 'fzj/mock/rs/v0.0.0/ddd-bbb',
+        '@id': 'fzj/mock/rs/v0.0.0/ddd-bbb',
         parcellations: [ mpWrong ]
       }
 
@@ -168,7 +179,8 @@ const getRegionInOtherTemplateSelectorBundle = (version: EnumParcRegVersion) =>
         mr0lh,
         mt3,
         mr0,
-        mr0rh
+        mr0rh,
+        mr0nh
       }
 
     }
@@ -180,7 +192,7 @@ const getRegionInOtherTemplateSelectorBundle = (version: EnumParcRegVersion) =>
       const mr1wrong = {
         labelIndex: 1,
         name: 'mr1',
-        fullId: {
+        id: {
           kg: {
             kgSchema: 'fzj/mock/pr',
             kgId: 'fff-bbb'
@@ -191,7 +203,7 @@ const getRegionInOtherTemplateSelectorBundle = (version: EnumParcRegVersion) =>
       const mr0wrong = {
         labelIndex: 1,
         name: 'mr0',
-        fullId: {
+        id: {
           kg: {
             kgSchema: 'fzj/mock/pr',
             kgId: 'aaa-fff'
@@ -203,8 +215,8 @@ const getRegionInOtherTemplateSelectorBundle = (version: EnumParcRegVersion) =>
       const mr1lh = {
         labelIndex: 1,
         name: 'mr1',
-        status: 'left hemisphere',
-        fullId: {
+        status: 'left',
+        id: {
           kg: {
             kgSchema: 'fzj/mock/pr',
             kgId: 'ccc-bbb'
@@ -215,8 +227,8 @@ const getRegionInOtherTemplateSelectorBundle = (version: EnumParcRegVersion) =>
       const mr1rh = {
         labelIndex: 1,
         name: 'mr1',
-        status: 'right hemisphere',
-        fullId: {
+        status: 'right',
+        id: {
           kg: {
             kgSchema: 'fzj/mock/pr',
             kgId: 'ccc-bbb'
@@ -224,11 +236,16 @@ const getRegionInOtherTemplateSelectorBundle = (version: EnumParcRegVersion) =>
         }
       }
 
+      const mr0nh = {
+        labelIndex: 11,
+        name: 'mr0',
+      }
+
       const mr0lh = {
         labelIndex: 1,
-        name: 'mr0',
-        status: 'left hemisphere',
-        fullId: {
+        name: 'mr0 left',
+        availableIn: [{id: 'fzj/mock/rs/v0.0.0/aaa-bbb'}, {id: 'fzj/mock/rs/v0.0.0/bbb-bbb'}, {id: 'fzj/mock/rs/v0.0.0/ccc-bbb'}],
+        id: {
           kg: {
             kgSchema: 'fzj/mock/pr',
             kgId: 'aaa-bbb'
@@ -238,9 +255,8 @@ const getRegionInOtherTemplateSelectorBundle = (version: EnumParcRegVersion) =>
 
       const mr0rh = {
         labelIndex: 1,
-        name: 'mr0',
-        status: 'right hemisphere',
-        fullId: {
+        name: 'mr0 right',
+        id: {
           kg: {
             kgSchema: 'fzj/mock/pr',
             kgId: 'aaa-bbb'
@@ -251,7 +267,7 @@ const getRegionInOtherTemplateSelectorBundle = (version: EnumParcRegVersion) =>
       const mr1 = {
         labelIndex: 1,
         name: 'mr1',
-        fullId: {
+        id: {
           kg: {
             kgSchema: 'fzj/mock/pr',
             kgId: 'ccc-bbb'
@@ -263,16 +279,19 @@ const getRegionInOtherTemplateSelectorBundle = (version: EnumParcRegVersion) =>
 
       const mp1h = {
         name: 'mp1h',
-        regions: [ mr1lh, mr0lh,  mr0rh, mr1rh ]
+        '@id': 'parcellation/id',
+        regions: [ mr0nh, mr1lh, mr0lh,  mr0rh, mr1rh ]
       }
 
       const mpWrong = {
         name: 'mp1h',
+        '@id': 'parcellation/id',
         regions: [ mr1wrong, mr0wrong ]
       }
 
       const mp0 = {
         name: 'mp0',
+        '@id': 'parcellation/id',
         regions: [ mr1, mr0 ]
       }
 
@@ -280,31 +299,31 @@ const getRegionInOtherTemplateSelectorBundle = (version: EnumParcRegVersion) =>
 
       const mt0 = {
         name: 'mt0',
-        fullId: 'fzj/mock/rs/v0.0.0/aaa-bbb',
+        '@id': 'fzj/mock/rs/v0.0.0/aaa-bbb',
         parcellations: [ mp0 ]
       }
 
       const mt1 = {
         name: 'mt1',
-        fullId: 'fzj/mock/rs/v0.0.0/bbb-bbb',
+        '@id': 'fzj/mock/rs/v0.0.0/bbb-bbb',
         parcellations: [ mp0 ]
       }
 
       const mt2 = {
         name: 'mt2',
-        fullId: 'fzj/mock/rs/v0.0.0/ccc-bbb',
+        '@id': 'fzj/mock/rs/v0.0.0/ccc-bbb',
         parcellations: [ mp1h ]
       }
 
       const mt3 = {
         name: 'mt3',
-        fullId: 'fzj/mock/rs/v0.0.0/ddd-bbb',
+        '@id': 'fzj/mock/rs/v0.0.0/ddd-bbb',
         parcellations: [ mp1h ]
       }
 
       const mtWrong = {
         name: 'mtWrong',
-        fullId: 'fzj/mock/rs/v0.0.0/ddd-bbb',
+        '@id': 'fzj/mock/rs/v0.0.0/ddd-bbb',
         parcellations: [ mpWrong ]
       }
 
@@ -317,6 +336,7 @@ const getRegionInOtherTemplateSelectorBundle = (version: EnumParcRegVersion) =>
         mt1,
         mp1h,
         mr0lh,
+        mr0nh,
         mt3,
         mr0,
         mr0rh
@@ -335,22 +355,22 @@ describe('> region.base.ts', () => {
     for (const enumKey of Object.keys(EnumParcRegVersion)) {
       describe(`> selector version for ${enumKey}`, () => {
 
-        const { mockFetchedTemplates, mr0, mt2, mt0, mp0, mt1, mp1h, mr0lh, mt3, mr0rh } = getRegionInOtherTemplateSelectorBundle(enumKey as EnumParcRegVersion)
+        const { mockFetchedTemplates, mr0, mt2, mt0, mp0, mt1, mp1h, mr0lh, mt3, mr0rh, mr0nh } = getRegionInOtherTemplateSelectorBundle(enumKey as EnumParcRegVersion)
 
         let selectedAtlas = {
           templateSpaces: mockFetchedTemplates
         }
         describe('> no hemisphere selected, simulates big brain cyto map', () => {
-  
+
           let result: any[]
           beforeAll(() => {
-            result = regionInOtherTemplateSelector.projector(selectedAtlas, mockFetchedTemplates, mt0, { region: mr0 })
+            result = regionInOtherTemplateSelector.projector(selectedAtlas, mockFetchedTemplates, { region: {...mr0, context: {template: mt0, parcellation: mp0} }})
           })
-    
+
           it('> length checks out', () => {
-            expect(result.length).toEqual(5)
+            expect(result.length).toEqual(4)
           })
-    
+
           it('> does not contain itself', () => {
             expect(result).not.toContain(
               jasmine.objectContaining({
@@ -360,7 +380,7 @@ describe('> region.base.ts', () => {
               })
             )
           })
-    
+
           it('> no hemisphere result has no hemisphere meta data', () => {
             expect(result).toContain(
               jasmine.objectContaining({
@@ -370,85 +390,65 @@ describe('> region.base.ts', () => {
               })
             )
           })
-    
+
           it('> hemisphere result has hemisphere metadata # 1', () => {
             expect(result).toContain(
               jasmine.objectContaining({
                 template: mt2,
                 parcellation: mp1h,
                 region: mr0lh,
-                hemisphere: 'left hemisphere'
+                hemisphere: 'left'
               })
             )
           })
           it('> hemisphere result has hemisphere metadata # 2', () => {
             expect(result).toContain(
               jasmine.objectContaining({
-                template: mt3,
-                parcellation: mp1h,
-                region: mr0lh,
-                hemisphere: 'left hemisphere'
-              })
-            )
-          })
-          it('> hemisphere result has hemisphere metadata # 3', () => {
-            expect(result).toContain(
-              jasmine.objectContaining({
-                template: mt3,
-                parcellation: mp1h,
-                region: mr0rh,
-                hemisphere: 'right hemisphere'
-              })
-            )
-          })
-          it('> hemisphere result has hemisphere metadata # 4', () => {
-            expect(result).toContain(
-              jasmine.objectContaining({
-                template: mt3,
+                template: mt2,
                 parcellation: mp1h,
                 region: mr0rh,
-                hemisphere: 'right hemisphere'
+                hemisphere: 'right'
               })
             )
           })
         })
-    
-        describe('> hemisphere data selected (left hemisphere), simulates julich-brain in mni152', () => {
+
+        describe('> hemisphere data selected (left), simulates julich-brain in mni152', () => {
           let result
           beforeAll(() => {
-            result = regionInOtherTemplateSelector.projector(selectedAtlas, mockFetchedTemplates, mt2, { region: mr0lh })
+            result = regionInOtherTemplateSelector.projector(selectedAtlas, mockFetchedTemplates, { region: {...mr0lh, context: {template: mt0, parcellation: mp1h} }})
           })
-    
+
           it('> length checks out', () => {
             expect(result.length).toEqual(3)
           })
-    
-          it('> does not select wrong hemisphere (right hemisphere)', () => {
+
+          it('> does not select wrong hemisphere (right)', () => {
             expect(result).not.toContain(
               jasmine.objectContaining({
-                template: mt3,
+                template: mt2,
                 parcellation: mp1h,
                 region: mr0rh,
               })
             )
           })
-    
-          it('> select the corresponding hemisphere (left hemisphere), but without hemisphere metadata', () => {
+
+          it('> select the region with correct hemisphere', () => {
             expect(result).toContain(
               jasmine.objectContaining({
-                template: mt3,
+                template: mt2,
                 parcellation: mp1h,
                 region: mr0lh
               })
             )
           })
         })
-      
+
       })
     }
 
   })
-  
+
   describe('> RegionBase', () => {
     let regionBase: RegionBase
     let mockStore: MockStore
@@ -519,7 +519,7 @@ describe('> region.base.ts', () => {
           expect(regionBase.position).toBeFalsy()
         })
       })
-    
+
       it('> populates if position property is array with length 3 and non NaN element', () => {
         regionBase.region = {
           ...mr0,
@@ -528,19 +528,110 @@ describe('> region.base.ts', () => {
         expect(regionBase.position).toBeTruthy()
       })
     })
-  
+
+    describe('> rgb', () => {
+      let strToRgbSpy: jasmine.Spy
+      let mockStore: MockStore
+      beforeEach(() => {
+        strToRgbSpy = spyOn(util, 'strToRgb')
+        mockStore = TestBed.inject(MockStore)
+        mockStore.overrideSelector(regionInOtherTemplateSelector, [])
+        mockStore.overrideSelector(getRegionParentParcRefSpace, { template: null, parcellation: null })
+      })
+
+      afterEach(() => {
+        strToRgbSpy.calls.reset()
+      })
+
+      it('> will take region.rgb if exists', () => {
+        const regionBase = new RegionBase(mockStore)
+        regionBase.region = {
+          rgb: [100, 120, 140]
+        }
+        expect(
+          regionBase.rgbString
+        ).toEqual(`rgb(100,120,140)`)
+      })
+
+      it('> if rgb not provided, and labelIndex > 65500, set to white', () => {
+
+        const regionBase = new RegionBase(mockStore)
+        regionBase.region = {
+          labelIndex: 65535
+        }
+        expect(
+          regionBase.rgbString
+        ).toEqual(`rgb(255,255,255)`)
+      })
+
+      describe('> if rgb not provided, labelIndex < 65500', () => {
+
+        describe('> arguments for strToRgb', () => {
+          it('> if ngId is defined, use ngId', () => {
+
+            const regionBase = new RegionBase(mockStore)
+            regionBase.region = {
+              ngId: 'foo',
+              name: 'bar',
+              labelIndex: 152
+            }
+            expect(strToRgbSpy).toHaveBeenCalledWith(`foo152`)
+          })
+          it('> if ngId is not defined, use name', () => {
+
+            const regionBase = new RegionBase(mockStore)
+            regionBase.region = {
+              name: 'bar',
+              labelIndex: 152
+            }
+            expect(strToRgbSpy).toHaveBeenCalledWith(`bar152`)
+          })
+        })
+
+        it('> calls strToRgb, and use return value for rgb', () => {
+          const getRandomNum = () => Math.floor(255*Math.random())
+          const arr = [
+            getRandomNum(),
+            getRandomNum(),
+            getRandomNum()
+          ]
+          strToRgbSpy.and.returnValue(arr)
+          const regionBase = new RegionBase(mockStore)
+          regionBase.region = {
+            foo: 'bar'
+          }
+          expect(
+            regionBase.rgbString
+          ).toEqual(`rgb(${arr.join(',')})`)
+        })
+
+        it('> if strToRgb returns falsy, uses fallback', () => {
+
+          strToRgbSpy.and.returnValue(null)
+          const regionBase = new RegionBase(mockStore)
+          regionBase.region = {
+            foo: 'bar'
+          }
+          expect(
+            regionBase.rgbString
+          ).toEqual(`rgb(255,200,200)`)
+        })
+      })
+    })
     describe('> changeView', () => {
       const fakeTmpl = {
+        '@id': 'faketmplid',
         name: 'fakeTmpl'
       }
       const fakeParc = {
+        '@id': 'fakeparcid',
         name: 'fakeParc'
       }
       beforeEach(() => {
         regionBase = new RegionBase(mockStore)
       })
 
-      describe('> if sameRegion has position attribute', () => {
+      describe('> [tmp] sameRegion to use transform backend', () => {
         let dispatchSpy: jasmine.Spy
 
         beforeEach(() => {
@@ -549,64 +640,106 @@ describe('> region.base.ts', () => {
         afterEach(() => {
           dispatchSpy.calls.reset()
         })
-        it('> malformed position is not an array > do not pass position', () => {
 
-          regionBase.changeView({
-            template: fakeTmpl,
-            parcellation: fakeParc,
-            region: {
-              position: 'hello wolrd'
-            }
-          })
-
-          expect(dispatchSpy).toHaveBeenCalledWith(
-            viewerStateNewViewer({
-              selectTemplate: fakeTmpl,
-              selectParcellation: fakeParc,
-              navigation: {}
-            })
-          )
-        })
-
-        it('> malformed position is an array of incorrect size > do not pass position', () => {
-
-          regionBase.changeView({
-            template: fakeTmpl,
-            parcellation: fakeParc,
-            region: {
-              position: []
-            }
-          })
-
-          expect(dispatchSpy).toHaveBeenCalledWith(
-            viewerStateNewViewer({
-              selectTemplate: fakeTmpl,
-              selectParcellation: fakeParc,
-              navigation: {}
-            })
-          )
-        })
+        it('> calls viewerStateSelectTemplateWithId', () => {
 
-        it('> correct position > pass position', () => {
           regionBase.changeView({
             template: fakeTmpl,
             parcellation: fakeParc,
-            region: {
-              position: [1,2,3]
-            }
           })
 
           expect(dispatchSpy).toHaveBeenCalledWith(
-            viewerStateNewViewer({
-              selectTemplate: fakeTmpl,
-              selectParcellation: fakeParc,
-              navigation: {
-                position: [1,2,3]
+            viewerStateSelectTemplateWithId({
+              payload: {
+                '@id': fakeTmpl['@id']
+              },
+              config: {
+                selectParcellation: {
+                  "@id": fakeParc['@id']
+                }
               }
             })
           )
         })
       })
+
+      /**
+       * currently, without position metadata, the navigation is broken
+       * fix changeView to fetch position metadata. If cannot, fallback to spatial backend
+       */
+
+      // describe('> if sameRegion has position attribute', () => {
+      //   let dispatchSpy: jasmine.Spy
+
+      //   beforeEach(() => {
+      //     dispatchSpy = spyOn(mockStore, 'dispatch')
+      //   })
+      //   afterEach(() => {
+      //     dispatchSpy.calls.reset()
+      //   })
+      //   it('> malformed position is not an array > do not pass position', () => {
+
+      //     regionBase.changeView({
+      //       template: fakeTmpl,
+      //       parcellation: fakeParc,
+      //       region: {
+      //         position: 'hello wolrd'
+      //       }
+      //     })
+
+      //     expect(dispatchSpy).toHaveBeenCalledWith(
+      //       viewerStateNewViewer({
+      //         selectTemplate: fakeTmpl,
+      //         selectParcellation: fakeParc,
+      //         navigation: {}
+      //       })
+      //     )
+      //   })
+
+      //   it('> malformed position is an array of incorrect size > do not pass position', () => {
+
+      //     regionBase.changeView({
+      //       template: fakeTmpl,
+      //       parcellation: fakeParc,
+      //       region: {
+      //         position: []
+      //       }
+      //     })
+
+      //     expect(dispatchSpy).toHaveBeenCalledWith(
+      //       viewerStateSelectTemplateWithId({
+      //         payload: {
+      //           '@id': fakeTmpl['@id']
+      //         },
+      //         config: {
+      //           selectParcellation: {
+      //             "@id": fakeParc['@id']
+      //           }
+      //         }
+      //       })
+      //     )
+      //   })
+
+      //   it('> correct position > pass position', () => {
+      //     regionBase.changeView({
+      //       template: fakeTmpl,
+      //       parcellation: fakeParc,
+      //       region: {
+      //         position: [1,2,3]
+      //       }
+      //     })
+
+      //     expect(dispatchSpy).toHaveBeenCalledWith(
+      //       viewerStateNewViewer({
+      //         selectTemplate: fakeTmpl,
+      //         selectParcellation: fakeParc,
+      //         navigation: {
+      //           position: [1,2,3]
+      //         }
+      //       })
+      //     )
+      //   })
+      // })
     })
   })
 })
diff --git a/src/ui/parcellationRegion/region.base.ts b/src/atlasComponents/parcellationRegion/region.base.ts
similarity index 71%
rename from src/ui/parcellationRegion/region.base.ts
rename to src/atlasComponents/parcellationRegion/region.base.ts
index 27a83cdf571b58c71d8de51e91de805e7332c77d..c69a2f94658760f599f896f16affca4a70ab277c 100644
--- a/src/ui/parcellationRegion/region.base.ts
+++ b/src/atlasComponents/parcellationRegion/region.base.ts
@@ -1,13 +1,13 @@
 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, map, withLatestFrom } from "rxjs/operators";
+import { distinctUntilChanged, switchMap, filter, map, withLatestFrom, 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, viewerStateNewViewer, isNewerThan } from "src/services/state/viewerState.store.helper";
+import { flattenRegions, getIdFromKgIdObj, rgbToHsl } from 'common/util'
+import { viewerStateSetConnectivityRegion, viewerStateNavigateToRegion, viewerStateToggleRegionSelect, viewerStateNewViewer, isNewerThan, viewerStateSelectTemplateWithId } from "src/services/state/viewerState.store.helper";
 import { viewerStateFetchedTemplatesSelector, viewerStateGetSelectedAtlas, viewerStateSelectedTemplateFullInfoSelector, viewerStateSelectedTemplateSelector } from "src/services/state/viewerState/selectors";
-import { intToRgb, verifyPositionArg, getRegionHemisphere } from 'common/util'
+import { strToRgb, verifyPositionArg, getRegionHemisphere } from 'common/util'
+import { getPosFromRegion } from "src/util/siibraApiConstants/fn";
 
 export class RegionBase {
 
@@ -33,10 +33,25 @@ export class RegionBase {
   set region(val) {
     this._region = val
     this.region$.next(this._region)
-    this.position = val && val.position
+
+    this.position = null
+    // bug the centroid returned is currently nonsense
+    // this.position = val?.props?.centroid_mm
     if (!this._region) return
 
-    const rgb = this._region.rgb || (this._region.labelIndex && intToRgb(Number(this._region.labelIndex))) || [255, 200, 200]
+    if (val?.position) {
+      this.position = val?.position 
+    }
+    const pos = getPosFromRegion(val)
+    if (pos) {
+      this.position = pos
+    }
+
+    const rgb = this._region.rgb
+      || (this._region.labelIndex > 65500 && [255, 255, 255])
+      || strToRgb(`${this._region.ngId || this._region.name}${this._region.labelIndex}`)
+      || [255, 200, 200]
+
     this.rgbString = `rgb(${rgb.join(',')})`
     const [_h, _s, l] = rgbToHsl(...rgb)
     this.rgbDarkmode = l < 0.4
@@ -45,8 +60,8 @@ export class RegionBase {
   get region(){
     return this._region
   }
-  
-  private region$: BehaviorSubject<any> = new BehaviorSubject(null)
+
+  public region$: BehaviorSubject<any> = new BehaviorSubject(null)
 
   @Input()
   public isSelected: boolean = false
@@ -166,30 +181,26 @@ export class RegionBase {
     const {
       template,
       parcellation,
-      region
     } = sameRegion
-    const { position } = region
-    const navigation = Array.isArray(position) && position.length === 3
-      ? { position }
-      : {  }
     this.closeRegionMenu.emit()
 
     /**
      * TODO use createAction in future
      * for now, not importing const because it breaks tests
      */
-    this.store$.dispatch(viewerStateNewViewer ({
-      selectTemplate: template,
-      selectParcellation: parcellation,
-      navigation,
-    }))
+    this.store$.dispatch(
+      viewerStateSelectTemplateWithId({
+        payload: {
+          '@id': template['@id'] || template['fullId']
+        },
+        config: {
+          selectParcellation: {
+            '@id': parcellation['@id'] || parcellation['fullId']
+          }
+        }
+      })
+    )
   }
-
-  public GO_TO_REGION_CENTROID = ARIA_LABELS.GO_TO_REGION_CENTROID
-  public SHOW_CONNECTIVITY_DATA = ARIA_LABELS.SHOW_CONNECTIVITY_DATA
-  public SHOW_IN_OTHER_REF_SPACE = ARIA_LABELS.SHOW_IN_OTHER_REF_SPACE
-  public SHOW_ORIGIN_DATASET = ARIA_LABELS.SHOW_ORIGIN_DATASET
-  public AVAILABILITY_IN_OTHER_REF_SPACE = ARIA_LABELS.AVAILABILITY_IN_OTHER_REF_SPACE
 }
 
 export const getRegionParentParcRefSpace = createSelector(
@@ -211,12 +222,12 @@ export const getRegionParentParcRefSpace = createSelector(
      */
     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
@@ -235,8 +246,8 @@ export const getRegionParentParcRefSpace = createSelector(
           parcellation: p
         }
       }
-    } 
-    
+    }
+
     return {
       template: null,
       parcellation: null
@@ -260,59 +271,77 @@ export class RenderViewOriginDatasetLabelPipe implements PipeTransform{
 export const regionInOtherTemplateSelector = createSelector(
   viewerStateGetSelectedAtlas,
   viewerStateFetchedTemplatesSelector,
-  viewerStateSelectedTemplateSelector,
-  (atlas, fetchedTemplates, templateSelected, prop) => {
-    const atlasTemplateSpacesIds = atlas.templateSpaces.map(({ ['@id']: id, fullId }) => id || fullId)
+  (atlas, fetchedTemplates, prop) => {
+    const atlasTemplateSpacesIds = atlas.templateSpaces.map(a => a['@id'])
     const { region: regionOfInterest } = prop
     const returnArr = []
 
     const regionOfInterestHemisphere = getRegionHemisphere(regionOfInterest)
 
-    const regionOfInterestId = getIdFromFullId(regionOfInterest.fullId)
-    if (!templateSelected) return []
-    const selectedTemplateId = getIdFromFullId(templateSelected.fullId)
-
     // need to ensure that the templates are defined in atlas definition
-    // atlas is the single source of truth 
-    
+    // atlas is the single source of truth
+
     const otherTemplates = fetchedTemplates
-      .filter(({ fullId }) => getIdFromFullId(fullId) !== selectedTemplateId)
-      .filter(({ ['@id']: id, fullId }) => atlasTemplateSpacesIds.includes(id || fullId))
+      .filter(({ ['@id']: id }) => id !== regionOfInterest.context.template['@id']
+          && atlasTemplateSpacesIds.includes(id)
+          && regionOfInterest.availableIn.map(ai => ai.id).includes(id))
+
     for (const template of otherTemplates) {
-      for (const parcellation of template.parcellations) {
-        const flattenedRegions = flattenRegions(parcellation.regions)
-        const selectableRegions = flattenedRegions.filter(({ labelIndex }) => !!labelIndex)
-
-        for (const region of selectableRegions) {
-          const id = getIdFromFullId(region.fullId)
-          if (!!id && id === regionOfInterestId) {
-            const regionHemisphere = getRegionHemisphere(region)
-            /**
+      const parcellation = template.parcellations.find(p => p['@id'] === regionOfInterest.context.parcellation['@id'])
+
+      const flattenedRegions = flattenRegions(parcellation.regions)
+      const selectableRegions = flattenedRegions.filter(({ labelIndex }) => !!labelIndex)
+
+      for (const region of selectableRegions) {
+        if (regionsEqual(regionOfInterest, region)) {
+
+          const regionHemisphere = getRegionHemisphere(region)
+
+          /**
              * if both hemisphere metadatas are defined
              */
-            if (
-              !!regionOfInterestHemisphere &&
+          if (
+            !!regionOfInterestHemisphere &&
               !!regionHemisphere
-            ) {
-              if (regionHemisphere === regionOfInterestHemisphere) {
-                returnArr.push({
-                  template,
-                  parcellation,
-                  region,
-                })
-              }
-            } else {
+          ) {
+            if (regionHemisphere === regionOfInterestHemisphere) {
               returnArr.push({
                 template,
                 parcellation,
                 region,
-                hemisphere: regionHemisphere
               })
             }
+          } else {
+            returnArr.push({
+              template,
+              parcellation,
+              region,
+              hemisphere: regionHemisphere
+            })
           }
         }
       }
+
     }
     return returnArr
   }
 )
+
+const regionsEqual = (region1, region2) => {
+  const region1Hemisphere = getRegionHemisphere(region1)
+  const region2Hemisphere = getRegionHemisphere(region2)
+
+  if (region1.id && region1.id.kg && region2.id && region2.id.kg) {
+    return getIdFromKgIdObj(region1.id.kg) === getIdFromKgIdObj(region2.id.kg)
+        // If both has hemispheres, they should be equal
+        && (!(region1Hemisphere && region2Hemisphere) || region1Hemisphere === region2Hemisphere)
+  }
+
+  if (region1Hemisphere && region2Hemisphere) {
+    return region1.name === region2.name
+  } else {
+    const region1NameBasis = region1Hemisphere? region1.name.substring(0, region1.name.lastIndexOf(' ')) : region1.name
+    const region2NameBasis = region2Hemisphere? region2.name.substring(0, region2.name.lastIndexOf(' ')) : region2.name
+    return region1NameBasis === region2NameBasis
+  }
+}
diff --git a/src/ui/parcellationRegion/region.directive.ts b/src/atlasComponents/parcellationRegion/region.directive.ts
similarity index 100%
rename from src/ui/parcellationRegion/region.directive.ts
rename to src/atlasComponents/parcellationRegion/region.directive.ts
diff --git a/src/ui/util.ts b/src/atlasComponents/parcellationRegion/regionAccordionTooltipText.pipe.ts
similarity index 88%
rename from src/ui/util.ts
rename to src/atlasComponents/parcellationRegion/regionAccordionTooltipText.pipe.ts
index 65c5f21990c6933682b4a511caa8882ea31cf805..c2a92b83de75ee5660c0d59a60e19f561581bf31 100644
--- a/src/ui/util.ts
+++ b/src/atlasComponents/parcellationRegion/regionAccordionTooltipText.pipe.ts
@@ -1,9 +1,5 @@
 import { Pipe, PipeTransform } from "@angular/core"
 
-/**
- * TODO find this pipe a home
- * not too sure where this should stay
- */
 @Pipe({
   name: 'regionAccordionTooltipTextPipe',
   pure: true
diff --git a/src/ui/parcellationRegion/regionListSimpleView/regionListSimpleView.component.ts b/src/atlasComponents/parcellationRegion/regionListSimpleView/regionListSimpleView.component.ts
similarity index 100%
rename from src/ui/parcellationRegion/regionListSimpleView/regionListSimpleView.component.ts
rename to src/atlasComponents/parcellationRegion/regionListSimpleView/regionListSimpleView.component.ts
diff --git a/src/ui/parcellationRegion/regionListSimpleView/regionListSimpleView.style.css b/src/atlasComponents/parcellationRegion/regionListSimpleView/regionListSimpleView.style.css
similarity index 100%
rename from src/ui/parcellationRegion/regionListSimpleView/regionListSimpleView.style.css
rename to src/atlasComponents/parcellationRegion/regionListSimpleView/regionListSimpleView.style.css
diff --git a/src/ui/parcellationRegion/regionListSimpleView/regionListSimpleView.template.html b/src/atlasComponents/parcellationRegion/regionListSimpleView/regionListSimpleView.template.html
similarity index 100%
rename from src/ui/parcellationRegion/regionListSimpleView/regionListSimpleView.template.html
rename to src/atlasComponents/parcellationRegion/regionListSimpleView/regionListSimpleView.template.html
diff --git a/src/ui/parcellationRegion/regionMenu/regionMenu.component.spec.ts b/src/atlasComponents/parcellationRegion/regionMenu/regionMenu.component.spec.ts
similarity index 65%
rename from src/ui/parcellationRegion/regionMenu/regionMenu.component.spec.ts
rename to src/atlasComponents/parcellationRegion/regionMenu/regionMenu.component.spec.ts
index fa2cc46ef39e251dad93d3a96cdcc6fa6c467112..91c25d47bedf7f1ceee968ea8d3f0b24b967f624 100644
--- a/src/ui/parcellationRegion/regionMenu/regionMenu.component.spec.ts
+++ b/src/atlasComponents/parcellationRegion/regionMenu/regionMenu.component.spec.ts
@@ -4,10 +4,11 @@ import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.modu
 import { UtilModule } from "src/util/util.module"
 import { CommonModule } from "@angular/common"
 import { provideMockStore } from "@ngrx/store/testing"
-import { RenderViewOriginDatasetLabelPipe } from '../region.base'
-import { Directive, Input } from "@angular/core"
+import { Component, Directive, Input } from "@angular/core"
 import { NoopAnimationsModule } from "@angular/platform-browser/animations"
 import { ComponentsModule } from "src/components"
+import { ParcellationRegionModule } from "../module"
+import { BS_ENDPOINT } from "src/util/constants"
 
 const mt0 = {
   name: 'mt0'
@@ -55,42 +56,22 @@ const hemisphereMrms = [ {
 
 const nohemisphereHrms = [mrm0, mrm1]
 
-@Directive({
-  selector: '[iav-dataset-preview-dataset-file]',
-  exportAs: 'iavDatasetPreviewDatasetFile'
+@Component({
+  selector: 'kg-regional-features-list',
+  template: ''
 })
-class MockPrvDsFileDirective {
-  @Input('iav-dataset-preview-dataset-file') 
-  file
-
-  @Input('iav-dataset-preview-dataset-file-filename') 
-  filefilename
-
-  @Input('iav-dataset-preview-dataset-file-dataset') 
-  filedataset
-
-  @Input('iav-dataset-preview-dataset-file-kgid') 
-  filekgid
 
-  @Input('iav-dataset-preview-dataset-file-kgschema') 
-  filekgschema
-
-  @Input('iav-dataset-preview-dataset-file-fullid') 
-  filefullid
-
-}
+class DummyKgRegionalFeatureList{}
 
 @Directive({
-  selector: '[single-dataset-directive]',
-  exportAs: 'singleDatasetDirective'
+  selector: '[kg-regional-features-list-directive]',
+  exportAs: 'kgRegionalFeaturesListDirective'
 })
 
 class DummySingleDatasetDirective{
   @Input()
-  kgId: string
+  region: string
 
-  @Input()
-  kgSchema: string
 }
 
 describe('> regionMenu.component.ts', () => {
@@ -104,18 +85,21 @@ describe('> regionMenu.component.ts', () => {
           CommonModule,
           NoopAnimationsModule,
           ComponentsModule,
+          ParcellationRegionModule,
         ],
         declarations: [
-          RegionMenuComponent,
-          RenderViewOriginDatasetLabelPipe,
           /**
            * Used by regionMenu.template.html to show region preview
            */
-          MockPrvDsFileDirective,
           DummySingleDatasetDirective,
+          DummyKgRegionalFeatureList,
         ],
         providers: [
-          provideMockStore({ initialState: {} })
+          provideMockStore({ initialState: {} }),
+          {
+            provide: BS_ENDPOINT,
+            useValue: 'http://example.dev/1_0'
+          }
         ]
       }).compileComponents()
       
diff --git a/src/atlasComponents/parcellationRegion/regionMenu/regionMenu.component.ts b/src/atlasComponents/parcellationRegion/regionMenu/regionMenu.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..879815ee4ae1d4a510927cde5193522dd5178aa9
--- /dev/null
+++ b/src/atlasComponents/parcellationRegion/regionMenu/regionMenu.component.ts
@@ -0,0 +1,73 @@
+import { Component, OnDestroy } from "@angular/core";
+import { Store } from "@ngrx/store";
+import { Observable, Subscription } from "rxjs";
+import { RegionBase } from '../region.base'
+import { CONST, ARIA_LABELS } from 'common/constants'
+import { ComponentStore } from "src/viewerModule/componentStore";
+
+@Component({
+  selector: 'region-menu',
+  templateUrl: './regionMenu.template.html',
+  styleUrls: ['./regionMenu.style.css'],
+  providers: [ ComponentStore ]
+})
+export class RegionMenuComponent extends RegionBase implements OnDestroy {
+
+  public CONST = CONST
+  public ARIA_LABELS = ARIA_LABELS
+  private subscriptions: Subscription[] = []
+
+  public activePanelTitles$: Observable<string[]>
+  private activePanelTitles: string[] = []
+  constructor(
+    store$: Store<any>,
+    private viewerCmpLocalUiStore: ComponentStore<{ activePanelsTitle: string[] }>,
+  ) {
+    super(store$)
+    this.viewerCmpLocalUiStore.setState({
+      activePanelsTitle: []
+    })
+
+    this.activePanelTitles$ = this.viewerCmpLocalUiStore.select(
+      state => state.activePanelsTitle
+    ) as Observable<string[]>
+
+    this.subscriptions.push(
+      this.activePanelTitles$.subscribe(
+        (activePanelTitles: string[]) => this.activePanelTitles = activePanelTitles
+      )
+    )
+  }
+
+  ngOnDestroy(): void {
+    this.subscriptions.forEach(s => s.unsubscribe())
+  }
+
+  handleExpansionPanelClosedEv(title: string){
+    this.viewerCmpLocalUiStore.setState({
+      activePanelsTitle: this.activePanelTitles.filter(n => n !== title)
+    })
+  }
+  handleExpansionPanelAfterExpandEv(title: string){
+    if (this.activePanelTitles.includes(title)) return
+    this.viewerCmpLocalUiStore.setState({
+      activePanelsTitle: [
+        ...this.activePanelTitles,
+        title
+      ]
+    })
+  }
+
+  public busyFlag = false
+  private busyMap = new Map<string, boolean>()
+  handleBusySignal(namespace: string, flag: boolean) {
+    this.busyMap.set(namespace, flag)
+    for (const [_key, val] of this.busyMap.entries()) {
+      if (val) {
+        this.busyFlag = true
+        return
+      }
+    }
+    this.busyFlag = false
+  }
+}
diff --git a/src/ui/parcellationRegion/regionMenu/regionMenu.style.css b/src/atlasComponents/parcellationRegion/regionMenu/regionMenu.style.css
similarity index 72%
rename from src/ui/parcellationRegion/regionMenu/regionMenu.style.css
rename to src/atlasComponents/parcellationRegion/regionMenu/regionMenu.style.css
index ee2db754e2ec22f48db4e1353f55895dd8aeb13a..34d152c481193d92a7b78b2fc6c0901f955556f9 100644
--- a/src/ui/parcellationRegion/regionMenu/regionMenu.style.css
+++ b/src/atlasComponents/parcellationRegion/regionMenu/regionMenu.style.css
@@ -20,10 +20,3 @@ mat-icon
   font-size: 95%;
   line-height: normal;
 }
-
-.header-container
-{
-  padding: 16px;
-  margin: -16px!important;
-  padding-top: 6rem;
-}
diff --git a/src/atlasComponents/parcellationRegion/regionMenu/regionMenu.template.html b/src/atlasComponents/parcellationRegion/regionMenu/regionMenu.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..52e6a76f5ef109bf25a83c722781dac41ac65722
--- /dev/null
+++ b/src/atlasComponents/parcellationRegion/regionMenu/regionMenu.template.html
@@ -0,0 +1,224 @@
+<ng-template [ngIf]="region">
+
+<mat-card class="mat-elevation-z4">
+  <!-- rgbDarkmode must be checked for strict equality to true/false 
+  as if rgb is undefined, rgbDarkmode will be null/undefined
+  which is falsy -->
+  <div class="sidenav-cover-header-container"
+    [ngClass]="{'darktheme': rgbDarkmode === true, 'lighttheme': rgbDarkmode === false}"
+    [style.backgroundColor]="rgbString">
+    <mat-card-title>
+      <div class="position-relative region-name iv-custom-comp text">
+        {{ region.name }}
+        <small *ngIf="region.status"> ({{region.status}})</small>
+      </div>
+    </mat-card-title>
+
+    <!-- subtitle on what it is -->
+    <mat-card-subtitle class="d-inline-flex align-items-center flex-wrap">
+      <mat-icon fontSet="fas" fontIcon="fa-brain"></mat-icon>
+      <span>
+        Brain region
+      </span>
+
+      <!-- origin datas format -->
+      <div *ngFor="let originDataset of (region.originDatasets || []); let index = index"
+        class="ml-2">
+        <i>&#183;</i>
+
+        <span *ngIf="originDataset?.format?.name as regionOrDsFormatName; else fallbackODsnameTmpl">
+          {{ regionOrDsFormatName }}
+        </span>
+        <ng-template #fallbackODsnameTmpl>
+          <span>
+            {{ regionOriginDatasetLabels$ | async | renderViewOriginDatasetlabel : index }}
+          </span>
+        </ng-template>
+      </div>
+
+      <mat-divider vertical="true" class="ml-2 h-2rem"></mat-divider>
+
+      <!-- position -->
+      <button mat-icon-button *ngIf="position"
+        (click)="navigateToRegion()"
+        [matTooltip]="ARIA_LABELS.GO_TO_REGION_CENTROID + ': ' + (position | nmToMm | addUnitAndJoin : 'mm')">
+        <mat-icon fontSet="fas" fontIcon="fa-map-marked-alt">
+        </mat-icon>
+      </button>
+
+      <!-- explore doi -->
+      <ng-template let-infos [ngIf]="region?.originDatainfos">
+        <ng-container *ngFor="let info of infos">
+          <a *ngFor="let url of info.urls"
+            [href]="url.doi | doiParserPipe"
+            [matTooltip]="ARIA_LABELS.EXPLORE_DATASET_IN_KG"
+            target="_blank"
+            mat-icon-button>
+            <i class="fas fa-external-link-alt"></i>
+          </a>
+        </ng-container>
+      </ng-template>
+
+    </mat-card-subtitle>
+
+  </div>
+</mat-card>
+
+<mat-accordion class="d-block mt-2">
+
+  <!-- description -->
+  <ng-template [ngIf]="(region.originDatainfos || []).length > 0">
+    <ng-container *ngFor="let originData of region.originDatainfos">
+      <ng-template #descTmpl>
+        <markdown-dom [markdown]="originData.description"
+          class="text-muted text-sm">
+        </markdown-dom>
+      </ng-template>
+
+      <ng-container *ngTemplateOutlet="ngMatAccordionTmpl; context: {
+        title: 'Description',
+        iconClass: 'fas fa-info',
+        iavNgIf: true,
+        content: descTmpl
+      }">
+
+      </ng-container>
+    </ng-container>
+  </ng-template>
+
+
+  <!-- Explore in other template -->
+  <ng-container *ngIf="regionInOtherTemplates$ | async as regionInOtherTemplates">
+
+    <ng-template #exploreInOtherTmpl>
+      <mat-card *ngFor="let sameRegion of regionInOtherTemplates"
+        class="p-0 border-0 box-shadow-none mt-1 tb-1 cursor-pointer"
+        (click)="changeView(sameRegion)"
+        [matTooltip]="sameRegion.template.name + (sameRegion.hemisphere ? (' - ' + sameRegion.hemisphere) : '')"
+        mat-ripple>
+        <small>
+          {{ sameRegion.template.name + (sameRegion.hemisphere ? (' - ' + sameRegion.hemisphere) : '') }}
+        </small>
+      </mat-card>
+    </ng-template>
+
+    <ng-container *ngTemplateOutlet="ngMatAccordionTmpl; context: {
+      title: 'Explore in other templates',
+      desc: regionInOtherTemplates.length,
+      iconClass: 'fas fa-brain',
+      iconTooltip: regionInOtherTemplates.length | regionAccordionTooltipTextPipe : 'regionInOtherTmpl',
+      iavNgIf: regionInOtherTemplates.length,
+      content: exploreInOtherTmpl
+    }">
+
+
+    </ng-container>
+  </ng-container>
+
+  <!-- kg regional features list -->
+  <ng-template #kgRegionalFeatureList>
+    <regional-feature-wrapper [region]="region$ | async">
+    </regional-feature-wrapper>
+  </ng-template>
+
+  <ng-container *ngTemplateOutlet="ngMatAccordionTmpl; context: {
+    title: CONST.REGIONAL_FEATURES,
+    iconClass: 'fas fa-database',
+    content: kgRegionalFeatureList,
+    desc: '',
+    iconTooltip: 'Regional Features',
+    iavNgIf: true
+  }">
+  </ng-container>
+
+  <!-- Connectivity -->
+  
+  <ng-template #connectivityContentTmpl let-expansionPanel="expansionPanel">
+    <mat-card-content class="flex-grow-1 flex-shrink-1 w-100">
+      <connectivity-browser class="pe-all flex-shrink-1"
+        [region]="region"
+        (setOpenState)="expansionPanel.expanded = $event"
+        [accordionExpanded]="expansionPanel.expanded"
+        (connectivityNumberReceived)="hasConnectivityDirective.connectivityNumber = $event">
+      </connectivity-browser>
+    </mat-card-content>
+  </ng-template>
+
+  <ng-container *ngTemplateOutlet="ngMatAccordionTmpl; context: {
+    title: 'Connectivity',
+    desc: hasConnectivityDirective.connectivityNumber,
+    iconClass: 'fas fa-braille',
+    iconTooltip: hasConnectivityDirective.connectivityNumber | regionAccordionTooltipTextPipe : 'connectivity',
+    iavNgIf: hasConnectivityDirective.hasConnectivity,
+    content: connectivityContentTmpl
+  }">
+  </ng-container>
+
+  <div has-connectivity
+    [region]="[region]"
+    #hasConnectivityDirective="hasConnectivityDirective">
+  </div>
+</mat-accordion>
+
+<div *ngIf="busyFlag" class="mt-2 d-flex justify-content-center">
+  <spinner-cmp></spinner-cmp>
+</div>
+
+<!-- expansion tmpl -->
+<ng-template #ngMatAccordionTmpl
+  let-title="title"
+  let-desc="desc"
+  let-iconClass="iconClass"
+  let-iconTooltip="iconTooltip"
+  let-iavNgIf="iavNgIf"
+  let-content="content">
+  <mat-expansion-panel
+    [expanded]="activePanelTitles$ | async | arrayContains : title"
+    [attr.data-opened]="expansionPanel.expanded"
+    [attr.data-mat-expansion-title]="title"
+    (closed)="handleExpansionPanelClosedEv(title)"
+    (afterExpand)="handleExpansionPanelAfterExpandEv(title)"
+    hideToggle
+    *ngIf="iavNgIf"
+    #expansionPanel="matExpansionPanel">
+
+    <mat-expansion-panel-header>
+
+      <!-- title -->
+      <mat-panel-title>
+        {{ title }}
+      </mat-panel-title>
+
+      <!-- desc + icon -->
+      <mat-panel-description class="d-flex align-items-center justify-content-end"
+        [matTooltip]="iconTooltip">
+        <span class="mr-3">{{ desc }}</span>
+        <span class="accordion-icon d-inline-flex justify-content-center">
+          <i [class]="iconClass"></i>
+        </span>
+      </mat-panel-description>
+
+    </mat-expansion-panel-header>
+
+    <!-- content -->
+    <ng-template matExpansionPanelContent>
+      <ng-container *ngTemplateOutlet="content; context: {
+        expansionPanel: expansionPanel
+      }">
+      </ng-container>
+    </ng-template>
+  </mat-expansion-panel>
+</ng-template>
+</ng-template>
+
+
+<ng-template [ngIf]="!region">
+  <mat-card class="mat-elevation-z4">
+    <h1 class="mat-h1 sidenav-cover-header-container">
+      <spinner-cmp class="d-inline-block"></spinner-cmp>
+      <span class="text-muted">
+        Loading region
+      </span>
+    </h1>
+  </mat-card>
+</ng-template>
\ No newline at end of file
diff --git a/src/ui/parcellationRegion/regionSimple/regionSimple.component.ts b/src/atlasComponents/parcellationRegion/regionSimple/regionSimple.component.ts
similarity index 100%
rename from src/ui/parcellationRegion/regionSimple/regionSimple.component.ts
rename to src/atlasComponents/parcellationRegion/regionSimple/regionSimple.component.ts
diff --git a/src/ui/parcellationRegion/regionSimple/regionSimple.style.css b/src/atlasComponents/parcellationRegion/regionSimple/regionSimple.style.css
similarity index 100%
rename from src/ui/parcellationRegion/regionSimple/regionSimple.style.css
rename to src/atlasComponents/parcellationRegion/regionSimple/regionSimple.style.css
diff --git a/src/ui/parcellationRegion/regionSimple/regionSimple.template.html b/src/atlasComponents/parcellationRegion/regionSimple/regionSimple.template.html
similarity index 100%
rename from src/ui/parcellationRegion/regionSimple/regionSimple.template.html
rename to src/atlasComponents/parcellationRegion/regionSimple/regionSimple.template.html
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/bsRegionInputBase.ts b/src/atlasComponents/regionalFeatures/bsFeatures/bsRegionInputBase.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6eed3fb1bcc28e7f59b3133acb4b354a90b5a0e7
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/bsRegionInputBase.ts
@@ -0,0 +1,46 @@
+import { BehaviorSubject, throwError } from "rxjs";
+import { map, switchMap } from "rxjs/operators";
+import { TRegion, IBSSummaryResponse, IBSDetailResponse } from "./type";
+import { BsFeatureService, TFeatureCmpInput } from "./service";
+import { flattenReducer } from 'common/util'
+import { Input } from "@angular/core";
+
+export class BsRegionInputBase{
+
+  protected region$ = new BehaviorSubject<TRegion>(null)
+  private _region: TRegion
+
+  @Input()
+  set region(val: TRegion) {
+    this._region = val
+    this.region$.next(val)
+  }
+
+  get region() {
+    return this._region
+  }
+
+  constructor(
+    private svc: BsFeatureService,
+    data?: TFeatureCmpInput
+  ){
+    if (data) {
+      this.region = data.region
+    }
+  }
+
+  protected featuresList$ = this.region$.pipe(
+    switchMap(region => this.svc.listFeatures(region)),
+    map(result => result.features.map(obj => Object.keys(obj).reduce(flattenReducer, [])))
+  )
+
+  protected getFeatureInstancesList<T extends keyof IBSSummaryResponse>(feature: T){
+    if (!this._region) return throwError('#getFeatureInstancesList region needs to be defined')
+    return this.svc.getFeatures<T>(feature, this._region)
+  }
+
+  protected getFeatureInstance<T extends keyof IBSDetailResponse>(feature: T, id: string) {
+    if (!this._region) return throwError('#getFeatureInstance region needs to be defined')
+    return this.svc.getFeature<T>(feature, this._region, id)
+  }
+}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/constants.ts b/src/atlasComponents/regionalFeatures/bsFeatures/constants.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3746ac94431b1b414a26a5e5fb1d6aa3b46ab0a4
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/constants.ts
@@ -0,0 +1,6 @@
+import { InjectionToken } from "@angular/core";
+import { Observable } from "rxjs";
+export { BS_ENDPOINT } from 'src/util/constants'
+
+export const BS_DARKTHEME = new InjectionToken<Observable<boolean>>('BS_DARKTHEME')
+export const REGISTERED_FEATURE_INJECT_DATA = new InjectionToken('REGISTERED_FEATURE_INJECT_DATA')
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/genericInfo/genericInfoCmp/genericInfo.component.ts b/src/atlasComponents/regionalFeatures/bsFeatures/genericInfo/genericInfoCmp/genericInfo.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d36cc47d41c7edf1b4101c2544bc0913ccdcadf9
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/genericInfo/genericInfoCmp/genericInfo.component.ts
@@ -0,0 +1,130 @@
+import { AfterViewInit, Component, Inject, Input, OnChanges, OnDestroy, Optional, TemplateRef, ViewChild, ViewContainerRef, ViewRef } from "@angular/core";
+import { BsRegionInputBase } from "../../bsRegionInputBase";
+import { KG_REGIONAL_FEATURE_KEY, TBSDetail, UNDER_REVIEW } from "../../kgRegionalFeature/type";
+import { ARIA_LABELS, CONST } from 'common/constants'
+import { TBSSummary } from "../../kgDataset";
+import { BsFeatureService } from "../../service";
+import { MAT_DIALOG_DATA } from "@angular/material/dialog";
+import { TDatainfos } from "src/util/siibraApiConstants/types";
+import { TRegion } from "../../type";
+
+/**
+ * this component is specifically used to render side panel ebrains dataset view
+ */
+
+export type TInjectableData = TDatainfos & {
+  dataType?: string
+  view?: ViewRef | TemplateRef<any>
+  region?: TRegion
+  summary?: TBSSummary
+  isGdprProtected?: boolean
+}
+
+@Component({
+  selector: 'generic-info-cmp',
+  templateUrl: './genericInfo.template.html',
+  styleUrls: [
+    './genericInfo.style.css'
+  ]
+})
+
+export class GenericInfoCmp extends BsRegionInputBase implements OnChanges, AfterViewInit, OnDestroy {
+
+  public ARIA_LABELS = ARIA_LABELS
+  public CONST = CONST
+
+  @Input()
+  public summary: TBSSummary
+
+  @Input()
+  public detail: TBSDetail
+
+  public loadingFlag = false
+  public error = null
+
+  public nameFallback = `[This dataset cannot be fetched right now]`
+  public isGdprProtected = false
+
+  public descriptionFallback = `[This dataset cannot be fetched right now]`
+  public useClassicUi = false
+  public dataType = 'ebrains regional dataset'
+
+  public description: string
+  public name: string
+  public urls: {
+    cite: string
+    doi: string
+  }[]
+
+  template: TemplateRef<any>
+  viewref: ViewRef
+
+  @ViewChild('insertViewTarget', { read: ViewContainerRef })
+  insertedViewVCR: ViewContainerRef
+
+  constructor(
+    svc: BsFeatureService,
+    @Optional() @Inject(MAT_DIALOG_DATA) data: TInjectableData
+  ){
+    super(svc)
+    if (data) {
+      const { dataType, description, name, urls, useClassicUi, view, region, summary, isGdprProtected } = data
+      this.description = description
+      this.name = name
+      this.urls = urls
+      this.useClassicUi = useClassicUi
+      if (dataType) this.dataType = dataType
+      if (typeof isGdprProtected !== 'undefined') this.isGdprProtected = isGdprProtected
+
+      if (!!view) {
+        if (view instanceof TemplateRef){
+          this.template = view
+        } else {
+          this.viewref = view
+        }
+      }
+
+      if (region && summary) {
+        this.region = region
+        this.summary = summary
+        this.ngOnChanges()
+      }
+    }
+  }
+
+  ngOnDestroy(){
+    this.insertedViewVCR.clear()
+  }
+
+  ngAfterViewInit(){
+    if (this.insertedViewVCR && this.viewref) {
+      this.insertedViewVCR.insert(this.viewref)
+    }
+  }
+
+  ngOnChanges(){
+    if (!this.region) return
+    if (!this.summary) return
+    if (!!this.detail) return
+    this.loadingFlag = true
+    this.getFeatureInstance(KG_REGIONAL_FEATURE_KEY, this.summary['@id']).subscribe(
+      detail => {
+        this.detail = detail
+
+        this.name = this.detail.src_name
+        this.description = this.detail.__detail?.description
+        this.urls = this.detail.__detail.kgReference.map(url => {
+          return { cite: null, doi: url }
+        })
+        
+        this.isGdprProtected = detail.__detail.embargoStatus && detail.__detail.embargoStatus.some(status => status["@id"] === UNDER_REVIEW["@id"])
+      },
+      err => {
+        this.error = err.toString()
+      },
+      () => {
+        this.loadingFlag = false
+      }
+    )
+  }
+}
diff --git a/src/atlasViewer/pluginUnit/pluginUnit.template.html b/src/atlasComponents/regionalFeatures/bsFeatures/genericInfo/genericInfoCmp/genericInfo.style.css
similarity index 100%
rename from src/atlasViewer/pluginUnit/pluginUnit.template.html
rename to src/atlasComponents/regionalFeatures/bsFeatures/genericInfo/genericInfoCmp/genericInfo.style.css
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/genericInfo/genericInfoCmp/genericInfo.template.html b/src/atlasComponents/regionalFeatures/bsFeatures/genericInfo/genericInfoCmp/genericInfo.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..ab89e43adb9a94f391b1a29a3f67fc893754ae3b
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/genericInfo/genericInfoCmp/genericInfo.template.html
@@ -0,0 +1,127 @@
+
+<!-- classic UI -->
+<ng-template [ngIf]="useClassicUi" [ngIfElse]="modernUi">
+  <mat-card-subtitle>
+    <ng-container *ngTemplateOutlet="nameTmpl">
+    </ng-container>
+  </mat-card-subtitle>
+
+
+  <!-- desc -->
+  <small>
+    <ng-container *ngTemplateOutlet="descTmpl"></ng-container>
+  </small>
+
+  <ng-container *ngTemplateOutlet="insertedView">
+  </ng-container>
+
+  <!-- footer -->
+  <mat-card-actions iav-media-query #iavMediaQuery="iavMediaQuery">
+    <ng-container *ngTemplateOutlet="actionBtnsTmpl; context: {
+      $implicit: (iavMediaQuery.mediaBreakPoint$ | async) > 1
+    }" >
+    </ng-container>
+  </mat-card-actions>
+
+  <ng-template #actionBtnsTmpl let-useSmallIcon>
+    <!-- explore -->
+    <ng-container>
+
+      <a *ngFor="let kgRef of (urls || [])"
+        [href]="kgRef.doi | doiParserPipe"
+        class="color-inherit"
+        mat-icon-button
+        [matTooltip]="ARIA_LABELS.EXPLORE_DATASET_IN_KG"
+        target="_blank">
+        <iav-dynamic-mat-button
+          [iav-dynamic-mat-button-style]="useSmallIcon ? 'mat-icon-button' : 'mat-raised-button'"
+          iav-dynamic-mat-button-color="primary">
+
+          <span *ngIf="!useSmallIcon">
+            Explore
+          </span>
+          <i class="fas fa-external-link-alt"></i>
+        </iav-dynamic-mat-button>
+      </a>
+    </ng-container>
+  </ng-template>
+</ng-template>
+
+<!-- modern UI -->
+<ng-template #modernUi>
+  <!-- header -->
+  <mat-card class="mat-elevation-z4">
+
+    <div class="sidenav-cover-header-container bg-50-grey-20">
+      <mat-card-title>
+        <ng-content select="[region-of-interest]"></ng-content>
+        <ng-container *ngTemplateOutlet="nameTmpl">
+        </ng-container>
+      </mat-card-title>
+
+      <mat-card-subtitle class="d-inline-flex align-items-center">
+        <mat-icon fontSet="fas" fontIcon="fa-database"></mat-icon>
+        <span>
+          {{ dataType }}
+        </span>
+
+        <button *ngIf="isGdprProtected"
+          [matTooltip]="CONST.GDPR_TEXT"
+          mat-icon-button color="warn">
+          <i class="fas fa-exclamation-triangle"></i>
+        </button>
+
+        <mat-divider [vertical]="true" class="ml-2 h-2rem"></mat-divider>
+
+        <!-- explore btn -->
+        <a *ngFor="let kgRef of (urls || [])"
+          [href]="kgRef.doi | doiParserPipe"
+          class="color-inherit"
+          mat-icon-button
+          [matTooltip]="ARIA_LABELS.EXPLORE_DATASET_IN_KG"
+          target="_blank">
+          <i class="fas fa-external-link-alt"></i>
+        </a>
+        
+      </mat-card-subtitle>
+    </div>
+
+  </mat-card>
+
+  <!-- description -->
+  <div class="text-muted d-block mat-body m-4"
+    *ngIf="!loadingFlag">
+    <ng-container *ngTemplateOutlet="descTmpl">
+    </ng-container>
+  </div>
+
+  <div>
+    <ng-container *ngTemplateOutlet="insertedView">
+    </ng-container>
+  </div>
+</ng-template>
+
+<ng-template #nameTmpl>
+  <span *ngIf="!loadingFlag; else isLoadingTmpl">
+    {{ name || nameFallback }}
+  </span>
+</ng-template>
+
+<!-- desc -->
+<ng-template #descTmpl>
+  <markdown-dom 
+    [markdown]="description || descriptionFallback">
+  </markdown-dom>
+</ng-template>
+
+<!-- inserted view -->
+<ng-template #insertedView>
+  <ng-template #insertViewTarget>
+
+  </ng-template>
+</ng-template>
+
+<!-- is loading tmpl -->
+<ng-template #isLoadingTmpl>
+  <spinner-cmp></spinner-cmp>
+</ng-template>
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/genericInfo/index.ts b/src/atlasComponents/regionalFeatures/bsFeatures/genericInfo/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d38b8440733fc665a74c75e3d9fa574ec90dfe26
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/genericInfo/index.ts
@@ -0,0 +1,2 @@
+export { GenericInfoModule } from './module'
+export { GenericInfoCmp, TInjectableData } from './genericInfoCmp/genericInfo.component'
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/genericInfo/module.ts b/src/atlasComponents/regionalFeatures/bsFeatures/genericInfo/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..22c71304167846bc7061e1f4d43c11becfb1cb14
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/genericInfo/module.ts
@@ -0,0 +1,65 @@
+import { CommonModule } from "@angular/common";
+import { Component, Inject, NgModule, Optional } from "@angular/core";
+import { MAT_DIALOG_DATA } from "@angular/material/dialog";
+import { ComponentsModule } from "src/components";
+import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module";
+import { UtilModule } from "src/util";
+import { IAV_DATASET_SHOW_DATASET_DIALOG_CMP } from "../kgDataset/showDataset/showDataset.directive";
+import { GenericInfoCmp } from "./genericInfoCmp/genericInfo.component";
+
+@Component({
+  selector: 'show-ds-dialog-cmp',
+  template: `
+<ng-template [ngIf]="useClassicUi" [ngIfElse]="modernUiTmpl">
+  <generic-info-cmp></generic-info-cmp>
+</ng-template>
+
+<ng-template #modernUiTmpl>
+
+  <mat-dialog-content class="m-0 p-0">
+    <generic-info-cmp></generic-info-cmp>
+  </mat-dialog-content>
+
+  <mat-dialog-actions align="center">
+    <button mat-button mat-dialog-close>
+      Close
+    </button>
+  </mat-dialog-actions>
+
+</ng-template>
+`
+})
+
+export class ShowDsDialogCmp{
+  public useClassicUi = false
+  constructor(
+    @Optional() @Inject(MAT_DIALOG_DATA) data: any
+  ){
+    this.useClassicUi = data.useClassicUi
+  }
+}
+
+@NgModule({
+  imports: [
+    CommonModule,
+    AngularMaterialModule,
+    UtilModule,
+    ComponentsModule,
+  ],
+  declarations: [
+    GenericInfoCmp,
+    ShowDsDialogCmp,
+  ],
+  exports: [
+    GenericInfoCmp,
+  ],
+
+  providers: [
+    {
+      provide: IAV_DATASET_SHOW_DATASET_DIALOG_CMP,
+      useValue: ShowDsDialogCmp
+    }
+  ]
+})
+
+export class GenericInfoModule{}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/ieegCmp/ieeg.component.ts b/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/ieegCmp/ieeg.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4694a43429ea69191d74fc69b4e2970cabdf1a67
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/ieegCmp/ieeg.component.ts
@@ -0,0 +1,153 @@
+import { Component, Inject, OnDestroy, Optional } from "@angular/core";
+import { Store } from "@ngrx/store";
+import { BehaviorSubject, forkJoin, merge, Observable, of, Subscription } from "rxjs";
+import { catchError, mapTo, switchMap } from "rxjs/operators";
+import { viewerStateAddUserLandmarks, viewerStateChangeNavigation, viewreStateRemoveUserLandmarks } from "src/services/state/viewerState/actions";
+import { BsRegionInputBase } from "../../bsRegionInputBase";
+import { REGISTERED_FEATURE_INJECT_DATA } from "../../constants";
+import { BsFeatureService, TFeatureCmpInput } from "../../service";
+import { TBSDEtail, TBSSummary, SIIBRA_FEATURE_KEY, TContactPoint, TElectrode } from '../type'
+import { ARIA_LABELS, CONST } from 'common/constants'
+
+@Component({
+  selector: 'bs-feature-ieeg.cmp',
+  templateUrl: './ieeg.template.html',
+  styleUrls: [
+    './ieeg.style.css'
+  ]
+})
+
+export class BsFeatureIEEGCmp extends BsRegionInputBase implements OnDestroy{
+
+  public ARIA_LABELS = ARIA_LABELS
+  public CONST = CONST
+
+  private featureId: string
+
+  private results: (TBSSummary & TBSDEtail)[] = []
+  constructor(
+    private store: Store<any>,
+    svc: BsFeatureService,
+    @Optional() @Inject(REGISTERED_FEATURE_INJECT_DATA) data: TFeatureCmpInput,
+  ){
+    super(svc, data)
+    if (data.featureId) this.featureId = data.featureId
+    this.subs.push(
+      this.results$.subscribe(results => {
+        this.results = results
+        this.loadLandmarks()
+      })
+    )
+  }
+
+  public results$: Observable<(TBSSummary & TBSDEtail)[]>  = this.region$.pipe(
+    switchMap(() => this.getFeatureInstancesList(SIIBRA_FEATURE_KEY).pipe(
+      switchMap(arr => forkJoin(arr.filter(it => {
+        if (!this.featureId) return true
+        return it['@id'] === this.featureId
+      }).map(it => this.getFeatureInstance(SIIBRA_FEATURE_KEY, it["@id"])))),
+      catchError(() => of([]))
+    )),
+  )
+
+  public busy$ = this.region$.pipe(
+    switchMap(() => merge(
+      of(true),
+      this.results$.pipe(
+        mapTo(false)
+      )
+    )),
+  )
+  
+  private subs: Subscription[] = []
+  ngOnDestroy(){
+    this.unloadLandmarks()
+    while(this.subs.length) this.subs.pop().unsubscribe()
+  }
+  private openElectrodeSet = new Set<TElectrode>() 
+  public openElectrode$ = new BehaviorSubject<TElectrode[]>([])
+  handleDatumExpansion(electrode: TElectrode, state: boolean) {
+    if (state) this.openElectrodeSet.add(electrode)
+    else this.openElectrodeSet.delete(electrode)
+    this.openElectrode$.next(Array.from(this.openElectrodeSet))
+    this.loadLandmarks()
+  }
+
+  private loadedLms: {
+    '@id': string
+    id: string
+    name: string
+    position: [number, number, number]
+    color: [number, number, number]
+    showInSliceView: boolean
+  }[] = []
+
+  private unloadLandmarks(){
+    /**
+     * unload all the landmarks first
+     */
+    this.store.dispatch(
+      viewreStateRemoveUserLandmarks({
+        payload: {
+          landmarkIds: this.loadedLms.map(l => l['@id'])
+        }
+      })
+    )
+  }
+
+  private loadLandmarks(){
+    this.unloadLandmarks()
+    this.loadedLms = []
+
+    const lms = [] as {
+      '@id': string
+      id: string
+      name: string
+      position: [number, number, number]
+      color: [number, number, number]
+      showInSliceView: boolean
+    }[]
+
+    for (const detail of this.results) {
+      for (const subjectKey in detail.electrodes){
+        const electrodes = detail.electrodes[subjectKey]
+        for (const electrodId in electrodes) {
+          const electrode = electrodes[electrodId]
+          if (!electrode.inRoi) continue
+          
+          for (const cpKey in electrode.contact_points) {
+            const cp = electrode.contact_points[cpKey]
+            lms.push({
+              "@id": `${detail.name}:${subjectKey}#${cpKey}`,
+              id: `${detail.name}:${subjectKey}#${cpKey}`,
+              name: `${detail.name}:${subjectKey}#${cpKey}`,
+              position: cp.location,
+              color: cp.inRoi ? [255, 100, 100]: [255, 255, 255],
+              showInSliceView: this.openElectrodeSet.has(electrode)
+            })
+          }
+        }
+      }
+    }
+    this.loadedLms = lms
+
+    this.store.dispatch(
+      viewerStateAddUserLandmarks({
+        landmarks: lms
+      })
+    )
+  }
+
+  handleContactPtClk(cp: TContactPoint) {
+    const { location } = cp
+    this.store.dispatch(
+      viewerStateChangeNavigation({
+        navigation: {
+          position: location.map(v => v * 1e6),
+          positionReal: true,
+          animation: {}
+        },
+      })
+    )
+  }
+}
diff --git a/src/ui/atlasDropdown/atlasDropdown.style.css b/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/ieegCmp/ieeg.style.css
similarity index 100%
rename from src/ui/atlasDropdown/atlasDropdown.style.css
rename to src/atlasComponents/regionalFeatures/bsFeatures/ieeg/ieegCmp/ieeg.style.css
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/ieegCmp/ieeg.template.html b/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/ieegCmp/ieeg.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..ff8c7d311b9fda82ac9131dab0bac41706274ba2
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/ieegCmp/ieeg.template.html
@@ -0,0 +1,76 @@
+<ng-template [ngIf]="busy$ | async" [ngIfElse]="contenttmpl">
+  <spinner-cmp></spinner-cmp>
+</ng-template>
+
+<ng-template #contenttmpl>
+  <ng-container *ngFor="let result of results$ | async">
+    <ng-container *ngFor="let subjectKeyVal of result | getProperty : 'electrodes' | keyvalue">
+
+      <ng-container *ngFor="let electrodeKeyVal of subjectKeyVal.value | keyvalue; let first = first">
+
+        <!-- subject id -->
+        <!-- <h4 *ngIf="first" class="mat-h4">
+          {{ subjectKeyVal.key }}
+        </h4> -->
+
+        <!-- electrodes -->
+        <ng-template [ngIf]="electrodeKeyVal.value.inRoi">
+          <ng-container *ngTemplateOutlet="electrodeTmpl; context: { $implicit: electrodeKeyVal.value }">
+          </ng-container>
+        </ng-template>
+      </ng-container>
+    </ng-container>
+  </ng-container>
+</ng-template>
+
+<!-- template for electrode -->
+<ng-template #electrodeTmpl let-electrode>
+
+  <mat-expansion-panel
+    [expanded]="openElectrode$ | async | arrayContains : electrode"
+    (opened)="handleDatumExpansion(electrode, true)"
+    (closed)="handleDatumExpansion(electrode, false)"
+    togglePosition="before">
+    <mat-expansion-panel-header>
+      <mat-panel-title>
+        Electrode
+      </mat-panel-title>
+      <mat-panel-description class="text-nowrap">
+        {{ electrode.electrode_id }}
+      </mat-panel-description>
+    </mat-expansion-panel-header>
+
+
+    <!-- <label for="task-list" class="d-block mat-h4 mt-4 text-muted">
+      Tasks
+    </label>
+    <section class="d-flex align-items-center mt-1">
+      <section id="task-list" class="flex-grow-1 flex-shrink-1 overflow-x-auto">
+        <div role="list">
+          <mat-chip *ngFor="let task of datum['tasks']" class="ml-1">
+            {{ task }}
+          </mat-chip>
+        </div>
+      </section>
+    </section> -->
+
+    <mat-divider></mat-divider>
+
+    <label for="contact-points-list" class="d-block mat-h4 mt-4 text-muted">
+      Contact Points
+    </label>
+    <section class="d-flex align-items-center mt-1">
+      <section id="contact-points-list" class="flex-grow-1 flex-shrink-1 overflow-x-auto">
+        <div role="list">
+          <mat-chip *ngFor="let cp_kv of electrode.contact_points | keyvalue"
+            [matTooltip]="cp_kv['value']['location']"
+            (click)="handleContactPtClk(cp_kv['value'])"
+            class="ml-1">
+            {{ cp_kv['key'] }}
+          </mat-chip>
+        </div>
+      </section>
+    </section>
+
+  </mat-expansion-panel>
+</ng-template>
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/ieegCtrl.directive.ts b/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/ieegCtrl.directive.ts
new file mode 100644
index 0000000000000000000000000000000000000000..85997dcd7100e63cd4289839b7bab29cdd2cbc43
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/ieegCtrl.directive.ts
@@ -0,0 +1,42 @@
+import { Directive, Inject, OnDestroy, Optional } from "@angular/core";
+import { merge, Observable, of, Subscription } from "rxjs";
+import { catchError, mapTo, switchMap } from "rxjs/operators";
+import { BsRegionInputBase } from "../bsRegionInputBase";
+import { REGISTERED_FEATURE_INJECT_DATA } from "../constants";
+import { BsFeatureService, TFeatureCmpInput } from "../service";
+import { IBSSummaryResponse, IRegionalFeatureReadyDirective } from "../type";
+import { SIIBRA_FEATURE_KEY } from './type'
+
+@Directive({
+  selector: '[bs-features-ieeg-directive]',
+  exportAs: 'bsFeatureIeegDirective'
+})
+
+export class BsFeatureIEEGDirective extends BsRegionInputBase implements IRegionalFeatureReadyDirective, OnDestroy{
+
+  public results$: Observable<IBSSummaryResponse['IEEG_Dataset'][]>  = this.region$.pipe(
+    switchMap(() => this.getFeatureInstancesList(SIIBRA_FEATURE_KEY).pipe(
+      catchError(() => of([]))
+    )),
+  )
+  public busy$ = this.region$.pipe(
+    switchMap(() => merge(
+      of(true),
+      this.results$.pipe(
+        mapTo(false)
+      )
+    ))
+  )
+
+  constructor(
+    svc: BsFeatureService,
+    @Optional() @Inject(REGISTERED_FEATURE_INJECT_DATA) data: TFeatureCmpInput,
+  ){
+    super(svc, data)
+  }
+
+  private sub: Subscription[] = []
+  ngOnDestroy(){
+    while(this.sub.length) this.sub.pop().unsubscribe()
+  }
+}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/index.ts b/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e4e8322a07b0f6a2a25ce0b8522e5d2550b2f09b
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/index.ts
@@ -0,0 +1,8 @@
+export {
+  BsFeatureIEEGModule
+} from './module'
+
+export {
+  IEEG_FEATURE_NAME,
+  SIIBRA_FEATURE_KEY,
+} from './type'
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/module.ts b/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0860745605fe927f2d60efae83855427a7b198dd
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/module.ts
@@ -0,0 +1,33 @@
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { ComponentsModule } from "src/components";
+import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module";
+import { UtilModule } from "src/util";
+import { BsFeatureService } from "../service";
+import { BsFeatureIEEGCmp } from "./ieegCmp/ieeg.component";
+import { BsFeatureIEEGDirective } from "./ieegCtrl.directive";
+import { IEEG_FEATURE_NAME } from "./type";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    ComponentsModule,
+    UtilModule,
+    AngularMaterialModule,
+  ],
+  declarations: [
+    BsFeatureIEEGCmp,
+    BsFeatureIEEGDirective
+  ]
+})
+
+export class BsFeatureIEEGModule{
+  constructor(svc: BsFeatureService){
+    svc.registerFeature({
+      name: IEEG_FEATURE_NAME,
+      icon: 'fas fa-info',
+      View: BsFeatureIEEGCmp,
+      Ctrl: BsFeatureIEEGDirective
+    })
+  }
+}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/type.ts b/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/type.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9029bf61dce493a6bb5f6fd927bfca92c28f9266
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/ieeg/type.ts
@@ -0,0 +1,39 @@
+export type TBSSummary = {
+  '@id': string
+  name: string
+  description: string
+}
+
+export type TContactPoint = {
+  id: string
+  location: [number, number, number]
+  inRoi?: boolean
+}
+
+export type TElectrode = {
+  electrode_id: string
+  subject_id: string
+  contact_points: {
+    [key: string]: TContactPoint
+  }
+  inRoi?: boolean
+}
+
+export type TBSDEtail = {
+  'kg_id': string
+  'electrodes': {
+    [key: string]: {
+      [key: string]: TElectrode
+    }
+  }
+}
+export type _TBSDEtail = {
+  '__kg_id': string
+  '__contact_points': {
+    [key: string]: TContactPoint
+  }
+}
+
+export const SIIBRA_FEATURE_KEY = 'IEEG_Dataset'
+export const _SIIBRA_FEATURE_KEY = 'IEEG_Electrode'
+export const IEEG_FEATURE_NAME = 'iEEG recordings'
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/index.ts b/src/atlasComponents/regionalFeatures/bsFeatures/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e571d3c4272b3c27af2343832de0f8d8aabd2399
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/index.ts
@@ -0,0 +1,5 @@
+export { BSFeatureModule } from './module'
+export { BS_ENDPOINT, BS_DARKTHEME } from './constants'
+export { TRegion } from './type'
+// nb do not export BsRegionInputBase from here
+// will result in cyclic imports
\ No newline at end of file
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/getTrailingHex.pipe.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/getTrailingHex.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5314267e7e1f31b8226ab6eea1ac890277781a68
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/getTrailingHex.pipe.ts
@@ -0,0 +1,13 @@
+import { Pipe, PipeTransform } from "@angular/core";
+
+@Pipe({
+  name: 'getTrailingHex',
+  pure: true
+})
+
+export class GetTrailingHexPipe implements PipeTransform{
+  public transform(input: string) {
+    const match = /[0-9a-f-]+$/.exec(input)
+    return match && match[0]
+  }
+}
\ No newline at end of file
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/index.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..337cdf646dd4827cc0f0a9a14f6637d0f4cb129e
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/index.ts
@@ -0,0 +1,2 @@
+export { KgDatasetModule } from './module'
+export { TCountedDataModality, TBSDetail, TBSSummary } from './type'
\ No newline at end of file
diff --git a/src/ui/databrowserModule/modalityPicker/modalityPicker.component.spec.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/modalityPicker/modalityPicker.component.spec.ts
similarity index 88%
rename from src/ui/databrowserModule/modalityPicker/modalityPicker.component.spec.ts
rename to src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/modalityPicker/modalityPicker.component.spec.ts
index f7313420c694f946111c9de6e6fd43f1a847e42a..9618701aaa39492065095230cc37d7b524d3fbec 100644
--- a/src/ui/databrowserModule/modalityPicker/modalityPicker.component.spec.ts
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/modalityPicker/modalityPicker.component.spec.ts
@@ -1,5 +1,5 @@
+import { TCountedDataModality } from "../type"
 import { SortModalityAlphabeticallyPipe } from "./modalityPicker.component"
-import { CountedDataModality } from "../databrowser.service"
 
 describe('> modalityPicker.component.ts', () => {
   describe('> ModalityPicker', () => {
@@ -8,7 +8,7 @@ describe('> modalityPicker.component.ts', () => {
 
   describe('> SortModalityAlphabeticallyPipe', () => {
 
-    const mods: CountedDataModality[] = [{
+    const mods: TCountedDataModality[] = [{
       name: 'bbb',
       occurance: 0,
       visible: false
diff --git a/src/ui/databrowserModule/modalityPicker/modalityPicker.component.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/modalityPicker/modalityPicker.component.ts
similarity index 74%
rename from src/ui/databrowserModule/modalityPicker/modalityPicker.component.ts
rename to src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/modalityPicker/modalityPicker.component.ts
index ff2ffc6bdaeab85b60ebf9288fa9743948a8cfdc..c00a636533018ec616110d4f366d967144254afa 100644
--- a/src/ui/databrowserModule/modalityPicker/modalityPicker.component.ts
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/modalityPicker/modalityPicker.component.ts
@@ -1,5 +1,5 @@
 import { Component, EventEmitter, Input, OnChanges, Output, Pipe, PipeTransform } from "@angular/core";
-import { CountedDataModality } from "../databrowser.service";
+import { TCountedDataModality } from "../type";
 import { ARIA_LABELS } from 'common/constants'
 
 
@@ -19,12 +19,12 @@ export class ModalityPicker implements OnChanges {
   public modalityVisibility: Set<string> = new Set()
 
   @Input()
-  public countedDataM: CountedDataModality[] = []
+  public countedDataM: TCountedDataModality[] = []
 
-  public checkedModality: CountedDataModality[] = []
+  public checkedModality: TCountedDataModality[] = []
 
   @Output()
-  public modalityFilterEmitter: EventEmitter<CountedDataModality[]> = new EventEmitter()
+  public modalityFilterEmitter: EventEmitter<TCountedDataModality[]> = new EventEmitter()
 
   // filter(dataentries:DataEntry[]) {
   //   return this.modalityVisibility.size === 0
@@ -40,7 +40,7 @@ export class ModalityPicker implements OnChanges {
    * TODO
    * togglemodailty should emit event, and let parent handle state
    */
-  public toggleModality(modality: Partial<CountedDataModality>) {
+  public toggleModality(modality: Partial<TCountedDataModality>) {
     this.modalityFilterEmitter.emit(
       this.countedDataM.map(d => d.name === modality.name
         ? {
@@ -67,7 +67,7 @@ export class ModalityPicker implements OnChanges {
   }
 }
 
-const sortByFn = (a: CountedDataModality, b: CountedDataModality) => (a.name || '0').toLowerCase().charCodeAt(0) - (b.name || '0').toLowerCase().charCodeAt(0) 
+const sortByFn = (a: TCountedDataModality, b: TCountedDataModality) => (a.name || '0').toLowerCase().charCodeAt(0) - (b.name || '0').toLowerCase().charCodeAt(0) 
 
 @Pipe({
   name: 'sortModalityAlphabetically',
@@ -75,7 +75,7 @@ const sortByFn = (a: CountedDataModality, b: CountedDataModality) => (a.name ||
 })
 
 export class SortModalityAlphabeticallyPipe implements PipeTransform{
-  public transform(arr: CountedDataModality[]): CountedDataModality[]{
+  public transform(arr: TCountedDataModality[]): TCountedDataModality[]{
     return [...arr].sort(sortByFn)
   }
 }
diff --git a/src/ui/databrowserModule/modalityPicker/modalityPicker.style.css b/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/modalityPicker/modalityPicker.style.css
similarity index 100%
rename from src/ui/databrowserModule/modalityPicker/modalityPicker.style.css
rename to src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/modalityPicker/modalityPicker.style.css
diff --git a/src/ui/databrowserModule/modalityPicker/modalityPicker.template.html b/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/modalityPicker/modalityPicker.template.html
similarity index 92%
rename from src/ui/databrowserModule/modalityPicker/modalityPicker.template.html
rename to src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/modalityPicker/modalityPicker.template.html
index e9aa626b7348aca8cb86085723943b01174d57c8..7bde04e8b2cc46f0872d978914bf9c254e79113a 100644
--- a/src/ui/databrowserModule/modalityPicker/modalityPicker.template.html
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/modalityPicker/modalityPicker.template.html
@@ -4,7 +4,7 @@
   </legend>
 
   <mat-checkbox
-    [ariaLabel]="datamodality.name"
+    
     [checked]="datamodality.visible"
     (change)="toggleModality(datamodality)"
     [ngClass]="{'muted': datamodality.occurance === 0}"
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/module.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d49cd9f3ca10dd6a966cd3d520012b9a48573746
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/module.ts
@@ -0,0 +1,26 @@
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { ShowDatasetDialogDirective } from "./showDataset/showDataset.directive";
+import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module";
+import { GetTrailingHexPipe } from "./getTrailingHex.pipe";
+import { ModalityPicker, SortModalityAlphabeticallyPipe } from "./modalityPicker/modalityPicker.component";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    AngularMaterialModule,
+  ],
+  declarations: [
+    ShowDatasetDialogDirective,
+    GetTrailingHexPipe,
+    ModalityPicker,
+    SortModalityAlphabeticallyPipe,
+  ],
+  exports: [
+    ShowDatasetDialogDirective,
+    GetTrailingHexPipe,
+    ModalityPicker,
+  ]
+})
+
+export class KgDatasetModule{}
diff --git a/src/ui/databrowserModule/showDatasetDialog.directive.spec.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/showDataset/showDataset.directive.spec.ts
similarity index 95%
rename from src/ui/databrowserModule/showDatasetDialog.directive.spec.ts
rename to src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/showDataset/showDataset.directive.spec.ts
index 18fa8349d210f0f86fdc82ecd8a2af4efaf57db4..b2e76a977405a529d82a74b6e66d49a2711ded3c 100644
--- a/src/ui/databrowserModule/showDatasetDialog.directive.spec.ts
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/showDataset/showDataset.directive.spec.ts
@@ -1,7 +1,7 @@
 import { Component } from "@angular/core";
 import { async, TestBed } from "@angular/core/testing";
-import { AngularMaterialModule } from "../sharedModules/angularMaterial.module";
-import { ShowDatasetDialogDirective, IAV_DATASET_SHOW_DATASET_DIALOG_CMP } from "./showDatasetDialog.directive";
+import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module";
+import { ShowDatasetDialogDirective, IAV_DATASET_SHOW_DATASET_DIALOG_CMP } from "./showDataset.directive";
 import { By } from "@angular/platform-browser";
 import { MatDialog } from "@angular/material/dialog";
 import { MatSnackBar } from "@angular/material/snack-bar";
@@ -115,6 +115,7 @@ describe('ShowDatasetDialogDirective', () => {
     expect(args[0]).toEqual(DummyDialogCmp)
     expect(args[1]).toEqual({
       ...ShowDatasetDialogDirective.defaultDialogConfig,
+      panelClass: ['no-padding-dialog'],
       data: {
         fullId: `minds/core/dataset/v1.0.0/aaa-bbb`
       }
@@ -151,6 +152,7 @@ describe('ShowDatasetDialogDirective', () => {
     expect(args[0]).toEqual(DummyDialogCmp)
     expect(args[1]).toEqual({
       ...ShowDatasetDialogDirective.defaultDialogConfig,
+      panelClass: ['no-padding-dialog'],
       data: {
         fullId: `abc/ccc-ddd`
       }
diff --git a/src/ui/databrowserModule/showDatasetDialog.directive.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/showDataset/showDataset.directive.ts
similarity index 63%
rename from src/ui/databrowserModule/showDatasetDialog.directive.ts
rename to src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/showDataset/showDataset.directive.ts
index 0cdd393e638bbca5f85ffa094521489ce8cdda5b..2cc1dae60470d227368d347a1dccda0b7529d124 100644
--- a/src/ui/databrowserModule/showDatasetDialog.directive.ts
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/showDataset/showDataset.directive.ts
@@ -1,7 +1,9 @@
-import { Directive, Input, HostListener, Inject, InjectionToken, Optional } from "@angular/core";
-import { MatDialog } from "@angular/material/dialog";
+import { Directive, HostListener, Inject, Input, Optional } from "@angular/core";
+import { MatDialog, MatDialogConfig } from "@angular/material/dialog";
 import { MatSnackBar } from "@angular/material/snack-bar";
 import { OVERWRITE_SHOW_DATASET_DIALOG_TOKEN, TOverwriteShowDatasetDialog } from "src/util/interfaces";
+import { TRegion as TSiibraRegion } from "src/util/siibraApiConstants/types";
+import { TRegion as TContextRegion } from 'src/atlasComponents/regionalFeatures/bsFeatures/type'
 
 export const IAV_DATASET_SHOW_DATASET_DIALOG_CMP = 'IAV_DATASET_SHOW_DATASET_DIALOG_CMP'
 export const IAV_DATASET_SHOW_DATASET_DIALOG_CONFIG = `IAV_DATASET_SHOW_DATASET_DIALOG_CONFIG`
@@ -10,10 +12,9 @@ export const IAV_DATASET_SHOW_DATASET_DIALOG_CONFIG = `IAV_DATASET_SHOW_DATASET_
   selector: '[iav-dataset-show-dataset-dialog]',
   exportAs: 'iavDatasetShowDatasetDialog'
 })
-
 export class ShowDatasetDialogDirective{
 
-  static defaultDialogConfig = {
+  static defaultDialogConfig: MatDialogConfig = {
     autoFocus: false
   }
 
@@ -32,10 +33,19 @@ export class ShowDatasetDialogDirective{
   @Input('iav-dataset-show-dataset-dialog-fullid')
   fullId: string
 
+  @Input('iav-dataset-show-dataset-dialog-urls')
+  urls: {
+    cite: string
+    doi: string
+  }[] = []
+
+  @Input('iav-dataset-show-dataset-dialog-contexted-region')
+  region: TSiibraRegion & TContextRegion
+
   constructor(
     private matDialog: MatDialog,
     private snackbar: MatSnackBar,
-    @Inject(IAV_DATASET_SHOW_DATASET_DIALOG_CMP) private dialogCmp: any,
+    @Optional() @Inject(IAV_DATASET_SHOW_DATASET_DIALOG_CMP) private dialogCmp: any,
     @Optional() @Inject(OVERWRITE_SHOW_DATASET_DIALOG_TOKEN) private overwriteFn: TOverwriteShowDatasetDialog
   ){ }
 
@@ -48,8 +58,8 @@ export class ShowDatasetDialogDirective{
         }
       }
       if (this.name || this.description) {
-        const { name, description } = this
-        return { name, description }
+        const { name, description, urls } = this
+        return { name, description, urls, useClassicUi: true }
       }
     })()
 
@@ -61,9 +71,12 @@ export class ShowDatasetDialogDirective{
       return this.overwriteFn(data)
     }
 
+    if (!this.dialogCmp) throw new Error(`IAV_DATASET_SHOW_DATASET_DIALOG_CMP not provided!`)
+    const { useClassicUi } = data
     this.matDialog.open(this.dialogCmp, {
       ...ShowDatasetDialogDirective.defaultDialogConfig,
-      data
+      data,
+      ...(useClassicUi ? {} : { panelClass: ['no-padding-dialog'] })
     })
   }
 }
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/type.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/type.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e56921ce5c5e1e9adc110ba666dcf1d2eb192be3
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/type.ts
@@ -0,0 +1,82 @@
+export type TCountedDataModality = {
+  name: string
+  occurance: number
+  visible: boolean
+}
+
+export type TBSSummary = {
+  ['@id']: string
+  src_name: string
+}
+
+export type TBSDetail = TBSSummary & {
+  __detail: {
+    formats: string[]
+    datasetDOI: {
+      cite: string
+      doi: string
+    }[]
+    activity: {
+      protocols: string[]
+      preparation: string[]
+    }[]
+    referenceSpaces: {
+      name: string
+      fullId: string
+    }[]
+    methods: string[]
+    custodians: {
+      "schema.org/shortName": string
+      identifier: string
+      name: string
+      '@id': string
+      shortName: string
+    }[]
+    project: string[]
+    description: string
+    parcellationAtlas: {
+      name: string
+      fullId: string
+      id: string[]
+    }[]
+    licenseInfo: {
+      name: string
+      url: string
+    }[]
+    embargoStatus: {
+      identifier: string[]
+      name: string
+      '@id': string
+    }[]
+    license: any[]
+    parcellationRegion: {
+      species: any[]
+      name: string
+      alias: string
+      fullId: string
+    }[]
+    species: string[]
+    name: string
+    files: {
+      byteSize: number
+      name: string
+      absolutePath: string
+      contentType: string
+    }[]
+    fullId: string
+    contributors: {
+      "schema.org/shortName": string
+      identifier: string
+      name: string
+      '@id': string
+      shortName: string
+    }[]
+    id: string
+    kgReference: string[] // aka doi
+    publications: {
+      name: string
+      cite: string
+      doi: string
+    }[]
+  }
+}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/index.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3efa1d80ed5927d3777da4cc28faad043ce7795b
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/index.ts
@@ -0,0 +1,11 @@
+export {
+  KgRegionalFeatureModule
+} from './module'
+
+export {
+  EbrainsRegionalFeatureName,
+  KG_REGIONAL_FEATURE_KEY,
+  UNDER_REVIEW,
+  TBSDetail,
+  TBSSummary
+} from './type'
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgRegList.component.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgRegList.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fd5b0afac18cbfd515e7ef21718e71084f75c0b2
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgRegList.component.ts
@@ -0,0 +1,89 @@
+import { ChangeDetectionStrategy, Component, Inject, Input, OnDestroy, Optional } from "@angular/core";
+import { BehaviorSubject, Subscription } from "rxjs";
+import { filter, switchMap, tap } from "rxjs/operators";
+import { TCountedDataModality } from '../../kgDataset'
+import { BsRegionInputBase } from "../../bsRegionInputBase";
+import { BsFeatureService, TFeatureCmpInput } from "../../service";
+import { KG_REGIONAL_FEATURE_KEY, TBSDetail, TBSSummary } from "../type";
+import { ARIA_LABELS } from 'common/constants'
+import { REGISTERED_FEATURE_INJECT_DATA } from "../../constants";
+
+@Component({
+  selector: 'kg-regional-features-list',
+  templateUrl: './kgRegList.template.html',
+  styleUrls: [
+    './kgRegList.style.css'
+  ],
+  changeDetection: ChangeDetectionStrategy.OnPush
+})
+
+export class KgRegionalFeaturesList extends BsRegionInputBase implements OnDestroy{
+
+  public ARIA_LABELS = ARIA_LABELS
+
+  public dataModalities: TCountedDataModality[] = []
+
+  @Input()
+  public disableVirtualScroll = false
+  
+  public visibleRegionalFeatures: TBSSummary[] = []
+  public kgRegionalFeatures: TBSSummary[] = []
+  public kgRegionalFeatures$ = this.region$.pipe(
+    filter(v => {
+      this.busy$.next(false)
+      return !!v
+    }),
+    // must not use switchmapto here
+    switchMap(() => {
+      this.busy$.next(true)
+      return this.getFeatureInstancesList(KG_REGIONAL_FEATURE_KEY).pipe(
+        tap(() => {
+          this.busy$.next(false)
+        })
+      )
+    })
+  )
+  constructor(
+    svc: BsFeatureService,
+    @Optional() @Inject(REGISTERED_FEATURE_INJECT_DATA) data: TFeatureCmpInput
+  ){
+    super(svc, data)
+    this.sub.push(
+      this.kgRegionalFeatures$.subscribe(val => {
+        this.kgRegionalFeatures = val
+        this.visibleRegionalFeatures = val
+      })
+    )
+  }
+  private sub: Subscription[] = []
+  ngOnDestroy(){
+    while (this.sub.length) this.sub.pop().unsubscribe()
+  }
+
+  public trackByFn(_index: number, dataset: TBSSummary) {
+    return dataset['@id']
+  }
+
+  public detailDict: {
+    [key: string]: TBSDetail
+  } = {}
+
+  public handlePopulatedDetailEv(detail: TBSDetail){
+    this.detailDict = {
+      ...this.detailDict,
+      [detail["@id"]]: detail
+    }
+    for (const method of detail.__detail.methods) {
+      const found = this.dataModalities.find(v => v.name === method)
+      if (found) found.occurance = found.occurance + 1
+      else this.dataModalities.push({
+        name: method,
+        occurance: 1,
+        visible: false
+      })
+    }
+    this.dataModalities = [...this.dataModalities]
+  }
+
+  public busy$ = new BehaviorSubject(false)
+}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgRegList.style.css b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgRegList.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..a0d49c32422a4789531e0cdaf7bf02fd4d43e20f
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgRegList.style.css
@@ -0,0 +1,9 @@
+cdk-virtual-scroll-viewport
+{
+  min-height: 24rem;
+}
+
+modality-picker
+{
+  font-size: 90%;
+}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgRegList.template.html b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgRegList.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..010f4c85971f683525a8f3a6ca7026d648ea0e45
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgRegList.template.html
@@ -0,0 +1,30 @@
+<spinner-cmp *ngIf="busy$ | async; else contentTmpl"></spinner-cmp>
+
+<ng-template #contentTmpl>
+  <cdk-virtual-scroll-viewport
+    [attr.aria-label]="ARIA_LABELS.LIST_OF_DATASETS_ARIA_LABEL"
+    class="h-100"
+    minBufferPx="200"
+    maxBufferPx="400"
+    itemSize="50">
+    <div *cdkVirtualFor="let dataset of visibleRegionalFeatures; trackBy: trackByFn; templateCacheSize: 20; let index = index"
+      class="h-50px overflow-hidden">
+
+      <!-- divider, show if not first -->
+      <mat-divider class="mt-1" *ngIf="index !== 0"></mat-divider>
+
+      <kg-regional-feature-summary
+        mat-ripple
+        iav-dataset-show-dataset-dialog
+        [iav-dataset-show-dataset-dialog-fullid]="dataset['@id']"
+        [iav-dataset-show-dataset-dialog-contexted-region]="region"
+        class="d-block pb-1 pt-1"
+        [region]="region"
+        [loadFull]="false"
+        [summary]="dataset"
+        (loadedDetail)="handlePopulatedDetailEv($event)">
+      </kg-regional-feature-summary>
+
+    </div>
+  </cdk-virtual-scroll-viewport>
+</ng-template>
\ No newline at end of file
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgReglist.directive.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgReglist.directive.ts
new file mode 100644
index 0000000000000000000000000000000000000000..df51bbb7f7c90d589e6e5c843d25f3a7f711f027
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgReglist.directive.ts
@@ -0,0 +1,59 @@
+import { Directive, EventEmitter, Inject, OnDestroy, Optional, Output } from "@angular/core";
+import { KG_REGIONAL_FEATURE_KEY, TBSSummary } from "../type";
+import { BsFeatureService, TFeatureCmpInput } from "../../service";
+import { BsRegionInputBase } from "../../bsRegionInputBase";
+import { merge, of, Subscription } from "rxjs";
+import { catchError, mapTo, startWith, switchMap, tap } from "rxjs/operators";
+import { IRegionalFeatureReadyDirective } from "../../type";
+import { REGISTERED_FEATURE_INJECT_DATA } from "../../constants";
+
+@Directive({
+  selector: '[kg-regional-features-list-directive]',
+  exportAs: 'kgRegionalFeaturesListDirective'
+})
+
+export class KgRegionalFeaturesListDirective extends BsRegionInputBase implements IRegionalFeatureReadyDirective, OnDestroy {
+  public kgRegionalFeatures: TBSSummary[] = []
+  public kgRegionalFeatures$ = this.region$.pipe(
+    // must not use switchmapto here
+    switchMap(() => {
+      this.busyEmitter.emit(true)
+      return this.getFeatureInstancesList(KG_REGIONAL_FEATURE_KEY).pipe(
+        catchError(() => of([])),
+        tap(() => {
+          this.busyEmitter.emit(false)
+        }),
+      )
+    }),
+    startWith([])
+  )
+  
+  constructor(
+    svc: BsFeatureService,
+    @Optional() @Inject(REGISTERED_FEATURE_INJECT_DATA) data: TFeatureCmpInput,  
+  ){
+    super(svc, data)
+    this.sub.push(
+      this.kgRegionalFeatures$.subscribe(val => {
+        this.kgRegionalFeatures = val
+      })
+    )
+  }
+  private sub: Subscription[] = []
+  ngOnDestroy(){
+    while (this.sub.length) this.sub.pop().unsubscribe()
+  }
+
+  results$ = this.kgRegionalFeatures$
+  busy$ = this.region$.pipe(
+    switchMap(() => merge(
+      of(true),
+      this.results$.pipe(
+        mapTo(false)
+      )
+    ))
+  )
+
+  @Output('kg-regional-features-list-directive-busy')
+  busyEmitter = new EventEmitter<boolean>()
+}
\ No newline at end of file
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegSummary/kgRegSummary.component.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegSummary/kgRegSummary.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..841226ad69f75fd821954f624878c4e1a4081de6
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegSummary/kgRegSummary.component.ts
@@ -0,0 +1,59 @@
+import { Component, EventEmitter, Input, OnChanges, Output } from "@angular/core";
+import { BsRegionInputBase } from "../../bsRegionInputBase";
+import { BsFeatureService } from "../../service";
+import { KG_REGIONAL_FEATURE_KEY, TBSDetail, TBSSummary } from '../type'
+
+@Component({
+  selector: 'kg-regional-feature-summary',
+  templateUrl: './kgRegSummary.template.html',
+  styleUrls: [
+    './kgRegSummary.style.css'
+  ],
+  exportAs: 'kgRegionalFeatureSummary'
+})
+
+export class KgRegSummaryCmp extends BsRegionInputBase implements OnChanges{
+
+  @Input()
+  public loadFull = false
+
+  @Input()
+  public summary: TBSSummary = null
+
+  public detailLoaded = false
+  public loadingDetail = false
+  public detail: TBSDetail = null
+  @Output()
+  public loadedDetail = new EventEmitter<TBSDetail>()
+
+  public error: string = null
+  @Output()
+  public errorEmitter = new EventEmitter<string>()
+
+  constructor(svc: BsFeatureService){
+    super(svc)
+  }
+
+  ngOnChanges(){
+    if (this.loadFull && !!this.summary) {
+      if (this.loadingDetail || this.detailLoaded) {
+        return
+      }
+      this.loadingDetail = true
+      this.getFeatureInstance(KG_REGIONAL_FEATURE_KEY, this.summary["@id"]).subscribe(
+        detail => {
+          this.detail = detail
+          this.loadedDetail.emit(detail)
+        },
+        err => {
+          this.error = err
+          this.errorEmitter.emit(err)
+        },
+        () => {
+          this.detailLoaded = true
+          this.loadingDetail = false
+        } 
+      )
+    }
+  }
+}
diff --git a/src/ui/config/currentLayout/currentLayout.style.css b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegSummary/kgRegSummary.style.css
similarity index 100%
rename from src/ui/config/currentLayout/currentLayout.style.css
rename to src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegSummary/kgRegSummary.style.css
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegSummary/kgRegSummary.template.html b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegSummary/kgRegSummary.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..5fcb11b2b8f7a550e66a3491430a0629d62f7533
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegSummary/kgRegSummary.template.html
@@ -0,0 +1,3 @@
+<small>
+  {{ summary.src_name }}
+</small>
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/module.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..dcf021cc7931ec67f1f17e2270ac90fe40e3c96b
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/module.ts
@@ -0,0 +1,50 @@
+import { CommonModule } from "@angular/common";
+import { Inject, NgModule, Optional } from "@angular/core";
+import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module";
+import { KgRegSummaryCmp } from "./kgRegSummary/kgRegSummary.component";
+import { KgRegionalFeaturesList } from "./kgRegList/kgRegList.component";
+import { KgRegionalFeaturesListDirective } from "./kgRegList/kgReglist.directive";
+import { KgDatasetModule } from "../kgDataset";
+import { UtilModule } from "src/util";
+import { ComponentsModule } from "src/components";
+import { BsFeatureService } from "../service";
+import { EbrainsRegionalFeatureName } from "./type";
+import { GENERIC_INFO_INJ_TOKEN } from "../type";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    AngularMaterialModule,
+    KgDatasetModule,
+    UtilModule,
+    ComponentsModule,
+  ],
+  declarations:[
+    KgRegSummaryCmp,
+    KgRegionalFeaturesList,
+    KgRegionalFeaturesListDirective,
+  ],
+  exports:[
+    KgRegSummaryCmp,
+    KgRegionalFeaturesList,
+    KgRegionalFeaturesListDirective,
+  ],
+})
+
+export class KgRegionalFeatureModule{
+  constructor(
+    svc: BsFeatureService,
+    @Optional() @Inject(GENERIC_INFO_INJ_TOKEN) Cmp: any
+  ){
+    if (!Cmp) {
+      console.warn(`GENERIC_INFO_INJ_TOKEN not injected!`)
+      return
+    }
+    svc.registerFeature({
+      name: EbrainsRegionalFeatureName,
+      icon: 'fas fa-ellipsis-h',
+      View: null,
+      Ctrl: KgRegionalFeaturesListDirective,
+    })
+  }
+}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/type.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/type.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f7ebe60367da3805b95b90c03237f31d4a23b4f2
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/type.ts
@@ -0,0 +1,10 @@
+export {
+  TBSDetail, TBSSummary
+} from '../kgDataset'
+
+export const EbrainsRegionalFeatureName = 'EBRAINS datasets'
+export const KG_REGIONAL_FEATURE_KEY = 'EbrainsRegionalDataset'
+
+export const UNDER_REVIEW = {
+  ['@id']: "https://nexus.humanbrainproject.org/v0/data/minds/core/embargostatus/v1.0.0/1d726b76-b176-47ed-96f0-b4f2e17d5f19"
+}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/module.ts b/src/atlasComponents/regionalFeatures/bsFeatures/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5851de10cb6b208b8725d8e273cf51aa2d75fd1d
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/module.ts
@@ -0,0 +1,43 @@
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { ComponentsModule } from "src/components";
+import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module";
+import { GenericInfoCmp, GenericInfoModule } from "./genericInfo";
+import { BsFeatureIEEGModule } from "./ieeg/module";
+import { KgRegionalFeatureModule } from "./kgRegionalFeature";
+import { GetBadgeFromFeaturePipe } from "./pipes/getBadgeFromFeature.pipe";
+import { RenderRegionalFeatureSummaryPipe } from "./pipes/renderRegionalFeatureSummary.pipe";
+import { BSFeatureReceptorModule } from "./receptor";
+import { RegionalFeatureWrapperCmp } from "./regionalFeatureWrapper/regionalFeatureWrapper.component";
+import { BsFeatureService } from "./service";
+import { GENERIC_INFO_INJ_TOKEN } from "./type";
+
+@NgModule({
+  imports: [
+    AngularMaterialModule,
+    CommonModule,
+    KgRegionalFeatureModule,
+    BSFeatureReceptorModule,
+    BsFeatureIEEGModule,
+    ComponentsModule,
+    GenericInfoModule,
+  ],
+  declarations: [
+    RegionalFeatureWrapperCmp,
+    RenderRegionalFeatureSummaryPipe,
+    GetBadgeFromFeaturePipe,
+  ],
+  providers: [
+    BsFeatureService,
+    {
+      provide: GENERIC_INFO_INJ_TOKEN,
+      useValue: GenericInfoCmp
+    }
+  ],
+  exports: [
+    RegionalFeatureWrapperCmp,
+    GenericInfoModule,
+  ]
+})
+
+export class BSFeatureModule{}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/pipes/getBadgeFromFeature.pipe.ts b/src/atlasComponents/regionalFeatures/bsFeatures/pipes/getBadgeFromFeature.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8fe67e163d116fb68ccbbff6524cb21d544db3f2
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/pipes/getBadgeFromFeature.pipe.ts
@@ -0,0 +1,36 @@
+import { Pipe, PipeTransform } from "@angular/core";
+import { IBSSummaryResponse, TContextedFeature } from "../type";
+import {
+  IEEG_FEATURE_NAME
+} from '../ieeg'
+import {
+  RECEPTOR_FEATURE_NAME
+} from '../receptor'
+
+export type TBadge = {
+  text: string
+  color: 'primary' | 'warn' | 'accent'
+}
+
+@Pipe({
+  name: 'getBadgeFromFeaturePipe',
+  pure: true
+})
+
+export class GetBadgeFromFeaturePipe implements PipeTransform{
+  public transform(input: TContextedFeature<keyof IBSSummaryResponse>): TBadge[]{
+    if (input.featureName === IEEG_FEATURE_NAME) {
+      return [{
+        text: IEEG_FEATURE_NAME,
+        color: 'primary',
+      }]
+    }
+    if (input.featureName === RECEPTOR_FEATURE_NAME) {
+      return [{
+        text: RECEPTOR_FEATURE_NAME,
+        color: 'accent',
+      }]
+    }
+    return []
+  }
+}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/pipes/renderRegionalFeatureSummary.pipe.ts b/src/atlasComponents/regionalFeatures/bsFeatures/pipes/renderRegionalFeatureSummary.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..829ed01981e3fb5a2061a058892f67742327dbb0
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/pipes/renderRegionalFeatureSummary.pipe.ts
@@ -0,0 +1,31 @@
+import { Pipe, PipeTransform } from "@angular/core";
+import { IBSSummaryResponse, TContextedFeature } from "../type";
+import {
+  IEEG_FEATURE_NAME
+} from '../ieeg'
+import {
+  RECEPTOR_FEATURE_NAME
+} from '../receptor'
+import {
+  EbrainsRegionalFeatureName
+} from '../kgRegionalFeature'
+
+@Pipe({
+  name: 'renderRegionalFeatureSummaryPipe',
+  pure: true,
+})
+
+export class RenderRegionalFeatureSummaryPipe implements PipeTransform{
+  public transform(input: TContextedFeature<keyof IBSSummaryResponse>): string{
+    if (input.featureName === IEEG_FEATURE_NAME) {
+      return input.result['name']
+    }
+    if (input.featureName === RECEPTOR_FEATURE_NAME) {
+      return input.result['name']
+    }
+    if (input.featureName === EbrainsRegionalFeatureName) {
+      return input.result['src_name']
+    }
+    return `[Unknown feature type]`
+  }
+}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/ar/autoradiograph.component.ts b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/ar/autoradiograph.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..efd93a3b9e0441b325afad42b041f48b1a6b7d24
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/ar/autoradiograph.component.ts
@@ -0,0 +1,56 @@
+import { Component, Input, OnChanges } from "@angular/core";
+import { BsFeatureReceptorBase } from "../base";
+import { CONST } from 'common/constants'
+import { TBSDetail } from "../type";
+
+const { RECEPTOR_AR_CAPTION } = CONST
+
+export function isAr(detail: TBSDetail, label: string){
+  if (label) return !!detail.__data.__autoradiographs[label]
+  return !!detail.__data.__autoradiographs
+}
+
+@Component({
+  selector: 'bs-features-receptor-autoradiograph',
+  templateUrl: './autoradiograph.template.html',
+  styleUrls: [
+    './autoradiograph.style.css'
+  ]
+})
+
+export class BsFeatureReceptorAR extends BsFeatureReceptorBase implements OnChanges {
+
+  public RECEPTOR_AR_CAPTION = RECEPTOR_AR_CAPTION
+  private DS_PREVIEW_URL = DATASET_PREVIEW_URL
+
+  @Input()
+  bsLabel: string
+
+  public imgUrl: string
+
+  constructor(){
+    super()
+  }
+  ngOnChanges(){
+    this.error = null
+    this.urls = []
+    if (!this.bsFeature) {
+      this.error = `bsFeature not populated`
+      return
+    }
+    if (!this.bsLabel) {
+      this.error = `bsLabel not populated`
+      return
+    }
+
+    const url = this.bsFeature.__data.__autoradiographs[this.bsLabel]
+    if (!url) {
+      this.error = `ar cannot be found`
+      return
+    }
+    this.urls = [{ url }]
+    const query = url.replace('https://object.cscs.ch/v1', '')
+    this.imgUrl = `${this.DS_PREVIEW_URL}/imageProxy/v1?u=${encodeURIComponent(query)}`
+    
+  }
+}
\ No newline at end of file
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/ar/autoradiograph.style.css b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/ar/autoradiograph.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..523e0a8d190409673b7c3b5d74a28d519554f1eb
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/ar/autoradiograph.style.css
@@ -0,0 +1,4 @@
+.ar-container > img
+{
+  width: 100%;
+}
\ No newline at end of file
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/ar/autoradiograph.template.html b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/ar/autoradiograph.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..1714d15fa96583c3aa2fcd4c19a474c5d42a6936
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/ar/autoradiograph.template.html
@@ -0,0 +1,22 @@
+<ng-template [ngIf]="error">
+  {{ error }}
+</ng-template>
+
+<a *ngFor="let url of urls"
+  [href]="url.url"
+  class="no-hover"
+  download>
+  <i class="fas fa-download"></i>
+  <span>
+    {{ url.text || (url.url | getFilenamePipe) }}
+  </span>
+</a>
+
+<figure>
+  <figcaption class="text-muted">
+    Autoradiograph: {{ RECEPTOR_AR_CAPTION }}
+  </figcaption>
+  <div class="ar-container">
+    <img [src]="imgUrl">
+  </div>
+</figure>
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/base.ts b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/base.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f4e418aa320f7d2e201955b5ab9a56e22ec39e78
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/base.ts
@@ -0,0 +1,17 @@
+import { Input } from "@angular/core";
+import { TBSDetail } from "./type";
+
+export class BsFeatureReceptorBase {
+  @Input()
+  bsFeature: TBSDetail
+
+  public urls: {
+    url: string
+    text?: string
+  }[] = []
+
+  public error = null
+
+  // eslint-disable-next-line @typescript-eslint/no-empty-function
+  constructor(){}
+}
\ No newline at end of file
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/entry/entry.component.ts b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/entry/entry.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7856a6ad7322c971c8afaef42181673255fa95ed
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/entry/entry.component.ts
@@ -0,0 +1,85 @@
+import { Component, Inject, OnDestroy, Optional } from "@angular/core";
+import { Observable, of, Subject, Subscription } from "rxjs";
+import { filter, map, shareReplay, startWith, switchMap, tap } from "rxjs/operators";
+import { BsRegionInputBase } from "../../bsRegionInputBase";
+import { REGISTERED_FEATURE_INJECT_DATA } from "../../constants";
+import { BsFeatureService, TFeatureCmpInput } from "../../service";
+import { TBSDetail } from "../type";
+import { ARIA_LABELS } from 'common/constants'
+import { isPr } from "../profile/profile.component";
+
+@Component({
+  selector: 'bs-features-receptor-entry',
+  templateUrl: './entry.template.html',
+  styleUrls: [
+    './entry.style.css'
+  ],
+})
+
+export class BsFeatureReceptorEntry extends BsRegionInputBase implements OnDestroy{
+
+  private sub: Subscription[] = []
+  public ARIA_LABELS = ARIA_LABELS
+
+  private selectedREntryId$ = new Subject<string>()
+  private _selectedREntryId: string
+  set selectedREntryId(id: string){
+    this.selectedREntryId$.next(id)
+    this._selectedREntryId = id
+  }
+  get selectedREntryId(){
+    return this._selectedREntryId
+  }
+
+  public selectedReceptor$: Observable<TBSDetail> = this.selectedREntryId$.pipe(
+    switchMap(id => id
+      ? this.getFeatureInstance('ReceptorDistribution', id)
+      : of(null)
+    ),
+    shareReplay(1),
+  )
+
+  public hasPrAr$: Observable<boolean> = this.selectedReceptor$.pipe(
+    map(detail => detail.__files.some(f => isPr(f))),
+  )
+
+  ngOnDestroy(){
+    while (this.sub.length > 0) this.sub.pop().unsubscribe()
+  }
+
+  public receptorsSummary$ = this.region$.pipe(
+    filter(v => !!v),
+    switchMap(() => this.getFeatureInstancesList('ReceptorDistribution')),
+    tap(arr => {
+      if (arr && arr.length > 0) {
+        this.selectedREntryId = arr[0]['@id']
+      } else {
+        this.selectedREntryId = null
+      }
+    }),
+    startWith([]),
+    shareReplay(1),
+  )
+
+  public onSelectReceptor(receptor: string){
+    this.selectedReceptor = receptor
+  }
+
+  public selectedReceptor = null
+  public allReceptors$ = this.selectedReceptor$.pipe(
+    map(rec => {
+      if (!rec) return []
+      return Object.keys(rec.__receptor_symbols || {})
+    })
+  )
+
+  constructor(
+    svc: BsFeatureService,
+    @Optional() @Inject(REGISTERED_FEATURE_INJECT_DATA) data: TFeatureCmpInput
+  ){
+    super(svc, data)
+    this.sub.push(
+      this.selectedReceptor$.subscribe()
+    )
+  }
+}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/entry/entry.style.css b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/entry/entry.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..71beb4683eec695ddf613bfcf2eecac35e79773d
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/entry/entry.style.css
@@ -0,0 +1,8 @@
+:host
+{
+  display: block;
+  width: 100%;
+  height: 100%;
+  padding-left:24px;
+  padding-right:24px;
+}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/entry/entry.template.html b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/entry/entry.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..813c489a6f2e2e054ed1019ebc36d5dc6ef2388c
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/entry/entry.template.html
@@ -0,0 +1,47 @@
+<mat-divider class="mt-2 mb-2"></mat-divider>
+
+<!-- potential selector for receptor data -->
+<mat-select class="invisible" [(value)]="selectedREntryId">
+  <mat-option *ngFor="let rec of receptorsSummary$ | async"
+    [value]="rec['@id']">
+    {{ rec.name }}
+  </mat-option>
+</mat-select>
+
+<ng-container *ngIf="selectedReceptor$ | async as selectedRec">
+
+  <bs-features-receptor-fingerprint
+    (onSelectReceptor)="onSelectReceptor($event)"
+    [bsFeature]="selectedRec">
+  </bs-features-receptor-fingerprint>
+
+  <ng-template [ngIf]="hasPrAr$ | async">
+    <mat-divider></mat-divider>
+
+    <mat-form-field class="mt-2 w-100">
+      <mat-label>
+        Select a receptor
+      </mat-label>
+      <mat-select [(value)]="selectedReceptor">
+        <mat-option
+          *ngFor="let receptorName of (allReceptors$ | async)"
+          [value]="receptorName">
+          {{ receptorName }}
+        </mat-option>
+      </mat-select>
+    </mat-form-field>
+  
+    <bs-features-receptor-profile
+      *ngIf="selectedReceptor"
+      [bsFeature]="selectedRec"
+      [bsLabel]="selectedReceptor">
+    </bs-features-receptor-profile>
+  
+    <bs-features-receptor-autoradiograph
+      *ngIf="selectedReceptor"
+      [bsFeature]="selectedRec"
+      [bsLabel]="selectedReceptor">
+    </bs-features-receptor-autoradiograph>
+  </ng-template>
+
+</ng-container>
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/fp/fp.component.ts b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/fp/fp.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a061221118bf8420bbf6883b14c15f18680c4fb8
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/fp/fp.component.ts
@@ -0,0 +1,90 @@
+import { ChangeDetectionStrategy, Component, ElementRef, EventEmitter, HostListener, Inject, OnChanges, OnDestroy, OnInit, Optional, Output } from "@angular/core";
+import { fromEvent, Observable, Subscription } from "rxjs";
+import { distinctUntilChanged, map } from "rxjs/operators";
+import { BS_DARKTHEME } from "../../constants";
+import { BsFeatureReceptorBase } from "../base";
+import { CONST } from 'common/constants'
+
+const { RECEPTOR_FP_CAPTION } = CONST
+
+@Component({
+  selector: 'bs-features-receptor-fingerprint',
+  templateUrl: './fp.template.html',
+  styleUrls: [
+    './fp.style.css'
+  ],
+  changeDetection: ChangeDetectionStrategy.OnPush
+})
+
+export class BsFeatureReceptorFingerprint extends BsFeatureReceptorBase implements OnChanges, OnInit, OnDestroy{
+
+  public RECEPTOR_FP_CAPTION = RECEPTOR_FP_CAPTION
+  private sub: Subscription[] = []
+
+  @HostListener('click')
+  onClick(){
+    if (this.selectedReceptor) {
+      this.onSelectReceptor.emit(this.selectedReceptor)
+    }
+  }
+
+  @Output()
+  public onSelectReceptor = new EventEmitter()
+  private selectedReceptor: any
+
+  constructor(
+    private elRef: ElementRef,
+    @Optional() @Inject(BS_DARKTHEME) public darktheme$: Observable<boolean>,
+  ){
+    super()
+  }
+
+  ngOnInit(){
+    // without, when devtool is out, runs sluggishly
+    // informing angular that change occurs here will be handled by programmer, and not angular
+
+    this.sub.push(
+      fromEvent<CustomEvent>(this.elRef.nativeElement, 'kg-ds-prv-regional-feature-mouseover').pipe(
+        map(ev => ev.detail?.data?.receptor?.label),
+        distinctUntilChanged(),
+      ).subscribe(label => {
+        this.selectedReceptor = label
+      })
+    )
+  }
+
+  ngOnDestroy() {
+    while (this.sub.length > 0) this.sub.pop().unsubscribe()
+  }
+
+  ngOnChanges(){
+    this.error = null
+    this.urls = []
+
+    if (!this.bsFeature) {
+      this.error = `bsFeature is not populated`
+      return
+    }
+
+    this.urls.push(
+      ...this.bsFeature.__files
+        .filter(u => /_fp_/.test(u))
+        .map(url => {
+          return {
+            url,
+          }
+        }),
+      ...this.bsFeature.__files
+        .filter(u => !/_pr_|_ar_/.test(u) && /receptors\.tsv$/.test(u))
+        .map(url => {
+          return {
+            url,
+          }
+        })
+    )
+
+    const radarEl = (this.elRef.nativeElement as HTMLElement).querySelector<any>('kg-dataset-dumb-radar')
+    radarEl.radarBs = this.bsFeature.__data.__fingerprint
+    radarEl.metaBs = this.bsFeature.__receptor_symbols
+  }
+}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/fp/fp.style.css b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/fp/fp.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..e2a98b2709c53c5853eebdbfc84370f371d84868
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/fp/fp.style.css
@@ -0,0 +1,18 @@
+kg-dataset-dumb-radar
+{
+  display: block;
+  min-height: 20em;
+}
+
+/* figure
+{
+  width: 100%;
+  height: 100%;
+}
+
+kg-dataset-dumb-radar
+{
+  display: block;
+  width: 100%;
+  height: 100%;
+} */
\ No newline at end of file
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/fp/fp.template.html b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/fp/fp.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..50d505459bb9893b0d7b98ce20f596ba2ba241a9
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/fp/fp.template.html
@@ -0,0 +1,22 @@
+<ng-template [ngIf]="error">
+  {{ error }}
+</ng-template>
+
+<a *ngFor="let url of urls"
+  [href]="url.url"
+  class="no-hover"
+  download>
+  <i class="fas fa-download"></i>
+  <span>
+    {{ url.text || (url.url | getFilenamePipe) }}
+  </span>
+</a>
+
+<figure>
+  <figcaption class="text-muted">
+    Fingerprint : {{ RECEPTOR_FP_CAPTION }}
+  </figcaption>
+  <kg-dataset-dumb-radar
+    [attr.kg-ds-prv-darkmode]="darktheme$ && (darktheme$ | async)">
+  </kg-dataset-dumb-radar>
+</figure>
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/hasReceptor.directive.ts b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/hasReceptor.directive.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6ef215934ac65dda3977d9e938d328ca45956f22
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/hasReceptor.directive.ts
@@ -0,0 +1,55 @@
+import { Directive, EventEmitter, Inject, OnDestroy, Optional, Output } from "@angular/core";
+import { merge, Observable, of, Subscription } from "rxjs";
+import { catchError, map, mapTo, switchMap } from "rxjs/operators";
+import { BsRegionInputBase } from "../bsRegionInputBase";
+import { REGISTERED_FEATURE_INJECT_DATA } from "../constants";
+import { BsFeatureService, TFeatureCmpInput } from "../service";
+import { IBSSummaryResponse, IRegionalFeatureReadyDirective } from "../type";
+
+@Directive({
+  selector: '[bs-features-receptor-directive]',
+  exportAs: 'bsFeatureReceptorDirective'
+})
+
+export class BsFeatureReceptorDirective extends BsRegionInputBase implements IRegionalFeatureReadyDirective, OnDestroy {
+  
+  private sub: Subscription[] = []
+
+  ngOnDestroy(){
+    while (this.sub.length > 0) this.sub.pop().unsubscribe()
+  }
+  public results$: Observable<IBSSummaryResponse['ReceptorDistribution'][]>  = this.region$.pipe(
+    switchMap(() => merge(
+      of([]),
+      this.getFeatureInstancesList('ReceptorDistribution').pipe(
+        catchError(() => of([]))
+      )
+    )),
+  )
+
+  public hasReceptor$ = this.results$.pipe(
+    map(arr => arr.length > 0)
+  )
+
+  public busy$ = this.region$.pipe(
+    switchMap(() => merge(
+      of(true),
+      this.results$.pipe(
+        mapTo(false)
+      )
+    ))
+  )
+  
+  constructor(
+    svc: BsFeatureService,
+    @Optional() @Inject(REGISTERED_FEATURE_INJECT_DATA) data: TFeatureCmpInput,
+  ){
+    super(svc, data)
+    this.sub.push(
+      this.busy$.subscribe(flag => this.fetchingFlagEmitter.emit(flag))
+    )
+  }
+
+  @Output('bs-features-receptor-directive-fetching-flag')
+  public fetchingFlagEmitter = new EventEmitter<boolean>()
+}
\ No newline at end of file
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/index.ts b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..79f8e55d97df512175ef1be0f913512bff41a16f
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/index.ts
@@ -0,0 +1,2 @@
+export { BSFeatureReceptorModule } from './module'
+export { RECEPTOR_FEATURE_NAME } from './type'
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/module.ts b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5527da1e82be817f075a17417509b7c218a865c1
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/module.ts
@@ -0,0 +1,46 @@
+import { CommonModule } from "@angular/common";
+import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from "@angular/core";
+import { FormsModule } from "@angular/forms";
+import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module";
+import { UtilModule } from "src/util";
+import { BsFeatureService } from "../service";
+import { BsFeatureReceptorAR } from "./ar/autoradiograph.component";
+import { BsFeatureReceptorEntry } from "./entry/entry.component";
+import { BsFeatureReceptorFingerprint } from "./fp/fp.component";
+import { BsFeatureReceptorDirective } from "./hasReceptor.directive";
+import { BsFeatureReceptorProfile } from "./profile/profile.component";
+import { RECEPTOR_FEATURE_NAME } from "./type";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    UtilModule,
+    AngularMaterialModule,
+    FormsModule,
+  ],
+  declarations: [
+    BsFeatureReceptorProfile,
+    BsFeatureReceptorAR,
+    BsFeatureReceptorFingerprint,
+    BsFeatureReceptorEntry,
+    BsFeatureReceptorDirective,
+  ],
+  exports: [
+    BsFeatureReceptorEntry,
+    BsFeatureReceptorDirective,
+  ],
+  schemas: [
+    CUSTOM_ELEMENTS_SCHEMA
+  ]
+})
+
+export class BSFeatureReceptorModule{
+  constructor(svc: BsFeatureService){
+    svc.registerFeature({
+      name: RECEPTOR_FEATURE_NAME,
+      icon: 'fas fa-info',
+      View: BsFeatureReceptorEntry,
+      Ctrl: BsFeatureReceptorDirective,
+    })
+  }
+}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/profile/profile.component.ts b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/profile/profile.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6b33a73b4810c5828752a783b006b0dbb6928b94
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/profile/profile.component.ts
@@ -0,0 +1,59 @@
+import { ChangeDetectionStrategy, Component, ElementRef, Inject, Input, OnChanges, Optional } from "@angular/core";
+import { Observable } from "rxjs";
+import { BS_DARKTHEME } from "../../constants";
+import { BsFeatureReceptorBase } from "../base";
+import { CONST } from 'common/constants'
+
+export function isPr(filename: string, label: string = ''){
+  return filename.indexOf(`_pr_${label}`) >= 0
+}
+
+const { RECEPTOR_PR_CAPTION } = CONST
+
+@Component({
+  selector: 'bs-features-receptor-profile',
+  templateUrl: './profile.template.html',
+  styleUrls: [
+    './profile.style.css'
+  ],
+  changeDetection: ChangeDetectionStrategy.OnPush
+})
+
+export class BsFeatureReceptorProfile extends BsFeatureReceptorBase implements OnChanges{
+  
+  public RECEPTOR_PR_CAPTION = RECEPTOR_PR_CAPTION
+
+  @Input()
+  bsLabel: string
+
+  constructor(
+    private elRef: ElementRef,
+    @Optional() @Inject(BS_DARKTHEME) public darktheme$: Observable<boolean>,
+  ){
+    super()
+  }
+
+  ngOnChanges(){
+    this.error = null
+    this.urls = []
+
+    if (!this.bsFeature) {
+      this.error = `bsFeature not populated`
+      return
+    }
+    if (!this.bsLabel) {
+      this.error = `bsLabel not populated`
+      return
+    }
+
+    this.urls = this.bsFeature.__files
+      .filter(url => isPr(url, this.bsLabel))
+      .map(url => {
+        return { url }
+      })
+
+    const profileBs = this.bsFeature.__data.__profiles[this.bsLabel]
+    const lineEl = (this.elRef.nativeElement as HTMLElement).querySelector<any>('kg-dataset-dumb-line')
+    lineEl.profileBs = profileBs
+  }
+}
diff --git a/src/ui/helpOnePager/helpOnePager.style.css b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/profile/profile.style.css
similarity index 100%
rename from src/ui/helpOnePager/helpOnePager.style.css
rename to src/atlasComponents/regionalFeatures/bsFeatures/receptor/profile/profile.style.css
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/profile/profile.template.html b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/profile/profile.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..0b71d2ac46cb92527b2a44af80a94a69961568b5
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/profile/profile.template.html
@@ -0,0 +1,22 @@
+<ng-template [ngIf]="error">
+  {{ error }}
+</ng-template>
+
+<a *ngFor="let url of urls"
+  [href]="url.url"
+  class="no-hover"
+  download>
+  <i class="fas fa-download"></i>
+  <span>
+    {{ url.text || (url.url | getFilenamePipe) }}
+  </span>
+</a>
+
+<figure>
+  <figcaption class="text-muted">
+    Profile: {{ RECEPTOR_PR_CAPTION }}
+  </figcaption>
+  <kg-dataset-dumb-line
+    [attr.kg-ds-prv-darkmode]="darktheme$ && (darktheme$ | async)">
+  </kg-dataset-dumb-line>
+</figure>
\ No newline at end of file
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/type.ts b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/type.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fd08389b38763bc8d6b032401323e2c7a0ed2154
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/type.ts
@@ -0,0 +1,57 @@
+type TReceptorCommon = {
+  latex: string
+  markdown: string
+  name: string
+}
+
+type TReceptor = string // TODO complete all possible neuroreceptor
+
+type TReceptorSymbol = {
+  [key: string]: {
+    receptor: TReceptorCommon
+    neurotransmitter: TReceptorCommon & { label: string }
+  }
+}
+
+type TProfile = {
+  [key: number]: number
+}
+
+type TBSFingerprint = {
+  unit: string
+  labels: TReceptor[]
+  meanvals: number[]
+  stdvals: number[]
+  n: 1
+}
+
+export type TBSSummary = {
+  ['@id']: string
+  name: string
+  info: string
+  origin_datainfos?: ({
+    name: string
+    description: string
+    urls: {
+      doi: string
+      cite?: string
+    }[]
+  })[]
+}
+
+export type TBSDetail = TBSSummary & {
+  __files: string[]
+  __receptor_symbols: TReceptorSymbol
+  __data: {
+    __profiles: {
+      [key: string]: TProfile
+    }
+    __autoradiographs: {
+      [key: string]: string
+    }
+    __fingerprint: TBSFingerprint
+    __profile_unit: string
+  }
+}
+
+export const RECEPTOR_FEATURE_NAME = 'receptor density'
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/regionalFeatureWrapper/regionalFeatureWrapper.component.ts b/src/atlasComponents/regionalFeatures/bsFeatures/regionalFeatureWrapper/regionalFeatureWrapper.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..72d4595fade4fec861614188df507ba3d5a42b56
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/regionalFeatureWrapper/regionalFeatureWrapper.component.ts
@@ -0,0 +1,211 @@
+import { Component, ComponentFactory, ComponentFactoryResolver, Inject, Injector, Input, OnChanges, OnDestroy, Optional, ViewChild, ViewContainerRef } from "@angular/core";
+import { IBSSummaryResponse, TContextedFeature, TRegion } from "../type";
+import { BsFeatureService, TFeatureCmpInput } from "../service";
+import { combineLatest, Observable, Subject } from "rxjs";
+import { debounceTime, map, shareReplay, startWith, tap } from "rxjs/operators";
+import { REGISTERED_FEATURE_INJECT_DATA } from "../constants";
+import { ARIA_LABELS } from 'common/constants'
+import {
+  IEEG_FEATURE_NAME
+} from '../ieeg'
+import {
+  RECEPTOR_FEATURE_NAME
+} from '../receptor'
+import {
+  EbrainsRegionalFeatureName
+} from '../kgRegionalFeature'
+import { OVERWRITE_SHOW_DATASET_DIALOG_TOKEN, TOverwriteShowDatasetDialog } from "src/util/interfaces";
+
+@Component({
+  selector: 'regional-feature-wrapper',
+  templateUrl: './regionalFeatureWrapper.template.html',
+  styleUrls: [
+    './regionalFeatureWrapper.style.css'
+  ]
+})
+
+export class RegionalFeatureWrapperCmp implements OnChanges, OnDestroy{
+
+  public useVirtualScroll = false
+
+  public ARIA_LABELS = ARIA_LABELS
+
+  @Input()
+  region: TRegion
+
+  @ViewChild('regionalFeatureContainerTmpl', { read: ViewContainerRef })
+  regionalFeatureContainerRef: ViewContainerRef
+
+  private weakmap = new WeakMap<(new () => any),  ComponentFactory<any>>()
+
+  private ondestroyCb: (() => void)[] = []
+  constructor(
+    private svc: BsFeatureService,
+    private cfr: ComponentFactoryResolver,
+    @Optional() @Inject(OVERWRITE_SHOW_DATASET_DIALOG_TOKEN) private overwriteFn: TOverwriteShowDatasetDialog
+  ){
+    const sub = this.registeredFeatures$.subscribe(arr => this.registeredFeatures = arr)
+    this.ondestroyCb.push(() => sub.unsubscribe())
+  }
+
+  private regionOnDestroyCb: (() => void)[] = []
+  private setupRegionalFeatureCtrl(){
+    if (!this.region) return
+    const { region } = this
+    for (const feat of this.svc.registeredFeatures){
+      const { name, icon } = feat
+      const ctrl = new feat.Ctrl(this.svc, { region })
+      const sub = combineLatest([
+        ctrl.busy$,
+        ctrl.results$.pipe(
+          startWith([])
+        )
+      ]).subscribe(
+        ([busy, results]) => {
+          this.registeredFeatureRawRegister[name] = { busy, results, icon }
+          this.registeredFeatureFireStream$.next(true)
+        }
+      )
+      this.regionOnDestroyCb.push(() => sub.unsubscribe())
+    }
+  }
+  private cleanUpRegionalFeature(){
+    while (this.regionOnDestroyCb.length) this.regionOnDestroyCb.pop()()
+    /**
+     * emit null to signify flush out of existing scan map
+     */
+    this.registeredFeatureRawRegister = {}
+    this.registeredFeatureFireStream$.next(true)
+  }
+
+  private registeredFeatureRawRegister: {
+    [key: string]: {
+      icon: string
+      busy: boolean
+      results: any[]
+    }
+  } = {}
+  private registeredFeatureFireStream$ = new Subject()
+  private registeredFeatureMasterStream$ = this.registeredFeatureFireStream$.pipe(
+    debounceTime(16),
+    /**
+     * must not use mapTo operator
+     * otherwise the emitted value will not change
+     */
+    map(() => this.registeredFeatureRawRegister),
+    shareReplay(1),
+  )
+  public busy$: Observable<boolean> = this.registeredFeatureMasterStream$.pipe(
+    map(obj => {
+      for (const key in obj) {
+        if(obj[key].busy) return true
+      }
+      return false
+    }),
+  )
+
+  public registeredFeatures: TContextedFeature<keyof IBSSummaryResponse>[] = []
+  private registeredFeatures$: Observable<TContextedFeature<keyof IBSSummaryResponse>[]> = this.registeredFeatureMasterStream$.pipe(
+    map(obj => {
+      const returnArr = []
+      for (const name in obj) {
+        if (obj[name].busy || obj[name].results.length === 0) {
+          continue
+        }
+        for (const result of obj[name].results) {
+          const objToBeInserted = {
+            featureName: name,
+            icon: obj[name].icon,
+            result
+          }
+          /**
+           * place ebrains regional features at the end
+           */
+          if (name === EbrainsRegionalFeatureName) {
+            returnArr.push(objToBeInserted)
+          } else {
+            returnArr.unshift(objToBeInserted)
+          }
+        }
+      }
+
+      return returnArr
+    }),
+  )
+
+  ngOnChanges(){
+    this.cleanUpRegionalFeature()
+    this.setupRegionalFeatureCtrl()
+  }
+
+  ngOnDestroy(){
+    this.cleanUpRegionalFeature()
+    while(this.ondestroyCb.length) this.ondestroyCb.pop()()
+  }
+
+  public handleFeatureClick(contextedFeature: TContextedFeature<any>){
+    if (!this.overwriteFn) {
+      console.warn(`show dialog function not overwritten!`)
+      return
+    }
+    
+    const featureId = contextedFeature.result['@id']
+    const arg = {}
+    if (contextedFeature.featureName === RECEPTOR_FEATURE_NAME) {
+      arg['name'] = contextedFeature.result['name']
+      arg['description'] = contextedFeature.result['info']
+      arg['urls'] = []
+      for (const info of contextedFeature.result['origin_datainfos']) {
+        arg['urls'].push(...info.urls)
+      }
+    }
+
+    if (contextedFeature.featureName === IEEG_FEATURE_NAME) {
+      arg['name'] = contextedFeature.result['name']
+      arg['description'] = contextedFeature.result['description'] || ' '
+      arg['isGdprProtected'] = true
+      arg['urls'] = []
+      for (const info of contextedFeature.result['origin_datainfos']) {
+        arg['urls'].push(...(info.urls || []))
+      }
+    }
+
+    if (contextedFeature.featureName === EbrainsRegionalFeatureName) {
+      arg['summary'] = contextedFeature.result
+    }
+
+    const { region } = this
+    
+    const feat = this.svc.registeredFeatures.find(f => f.name === contextedFeature.featureName)
+    if (!feat) {
+      console.log(`cannot find feature with name ${contextedFeature.featureName}`)
+      return
+    }
+    
+    const cf = (() => {
+      if (!feat.View) return null
+      const mapped = this.weakmap.get(feat.View)
+      if (mapped) return mapped
+      const _cf = this.cfr.resolveComponentFactory(feat.View)
+      this.weakmap.set(feat.View ,_cf)
+      return _cf
+    })()
+
+    this.overwriteFn({
+      region,
+      dataType: contextedFeature.featureName,
+      view: (() => {
+        if (!cf) return null
+        const injector = Injector.create({
+          providers: [{
+            provide: REGISTERED_FEATURE_INJECT_DATA,
+            useValue: { region, featureId } as TFeatureCmpInput
+          }],
+        })
+        const cmp = cf.create(injector)
+        return cmp.hostView
+      })(),
+      ...arg,
+    })
+  }
+}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/regionalFeatureWrapper/regionalFeatureWrapper.style.css b/src/atlasComponents/regionalFeatures/bsFeatures/regionalFeatureWrapper/regionalFeatureWrapper.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..816e7ba25d2192ebd17900e60655db92bf17063b
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/regionalFeatureWrapper/regionalFeatureWrapper.style.css
@@ -0,0 +1,19 @@
+.button-text
+{
+  white-space: normal;
+  line-height: 1.5rem;
+  text-align: center;
+}
+
+.feature-container,
+cdk-virtual-scroll-viewport
+{
+  min-height: 24rem;
+}
+
+
+.feature-container
+{
+  height: 24rem;
+  overflow-y: scroll;
+}
\ No newline at end of file
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/regionalFeatureWrapper/regionalFeatureWrapper.template.html b/src/atlasComponents/regionalFeatures/bsFeatures/regionalFeatureWrapper/regionalFeatureWrapper.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..6a62ae958982689268974f1fbfab9047dce5188e
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/regionalFeatureWrapper/regionalFeatureWrapper.template.html
@@ -0,0 +1,77 @@
+<ng-template [ngTemplateOutlet]="resultTmpl">
+</ng-template>
+
+<ng-template #busyTmpl>
+  <spinner-cmp></spinner-cmp>
+</ng-template>
+
+<ng-template #resultTmpl>
+  
+  <!-- virtual scroll. do not activate until autosize is supported -->
+  <cdk-virtual-scroll-viewport
+    *ngIf="useVirtualScroll; else regularScrollTmpl"
+    [attr.aria-label]="ARIA_LABELS.LIST_OF_DATASETS_ARIA_LABEL"
+    class="h-100"
+    minBufferPx="200"
+    maxBufferPx="400"
+    itemSize="50">
+    <div *cdkVirtualFor="let feature of registeredFeatures; templateCacheSize: 20; let index = index"
+      class="h-50px overflow-hidden">
+
+      <!-- divider, show if not first -->
+      <mat-divider *ngIf="index !== 0"></mat-divider>
+      <ng-container *ngTemplateOutlet="itemContainer; context: { $implicit: feature }">
+      </ng-container>
+
+    </div>
+  </cdk-virtual-scroll-viewport>
+
+  <!-- fallback, regular scroll -->
+  <!-- less efficient on large list, but for now should do -->
+  <ng-template #regularScrollTmpl>
+    <div class="feature-container"
+      [attr.aria-label]="ARIA_LABELS.LIST_OF_DATASETS_ARIA_LABEL">
+
+      <!-- if busy, show spinner -->
+      <ng-template [ngIf]="busy$ | async">
+        <ng-template [ngTemplateOutlet]="busyTmpl"></ng-template>
+      </ng-template>
+      <div *ngFor="let feature of registeredFeatures; let index = index"
+        class="overflow-hidden">
+
+        <!-- divider, show if not first -->
+        <mat-divider *ngIf="index !== 0"></mat-divider>
+        <ng-container *ngTemplateOutlet="itemContainer; context: { $implicit: feature }">
+        </ng-container>
+
+      </div>
+    </div>
+  </ng-template>
+  
+</ng-template>
+
+<!-- feature template -->
+<ng-template #itemContainer let-feature>
+  <div class="d-inline-block pt-4 cursor-default"
+    (click)="handleFeatureClick(feature)"
+    mat-ripple>
+
+    <!-- mat-chip container -->
+    <!-- do not use mat-chip-list to avoid adding incorrect a11y info -->
+    <div class="transform-origin-left-center scale-80">
+      <mat-chip *ngFor="let badge of feature | getBadgeFromFeaturePipe"
+        [color]="badge.color"
+        selected>
+        {{ badge.text }}
+      </mat-chip>
+    </div>
+
+    <small>
+      {{ feature | renderRegionalFeatureSummaryPipe }}
+    </small>
+  </div>
+</ng-template>
+
+<!-- dummy container -->
+<ng-template #regionalFeatureContainerTmpl>
+</ng-template>
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/service.ts b/src/atlasComponents/regionalFeatures/bsFeatures/service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7f52201e0619227b8ba17f23d0b4ce6cb710ce23
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/service.ts
@@ -0,0 +1,140 @@
+import { HttpClient } from "@angular/common/http";
+import { Inject, Injectable } from "@angular/core";
+import { BehaviorSubject } from "rxjs";
+import { shareReplay } from "rxjs/operators";
+import { CachedFunction } from "src/util/fn";
+import { BS_ENDPOINT } from "./constants";
+import { IBSSummaryResponse, IBSDetailResponse, TRegion, IFeatureList, IRegionalFeatureReadyDirective } from './type'
+import { SIIBRA_FEATURE_KEY as IEEG_FEATURE_KEY } from '../bsFeatures/ieeg/type'
+
+function processRegion(region: TRegion) {
+  return `${region.name} ${region.status ? region.status : '' }`
+}
+
+export type TFeatureCmpInput = {
+  region: TRegion
+  featureId?: string
+}
+
+export type TRegisteredFeature<V = any> = {
+  name: string
+  icon: string // fontawesome font class, e.g. `fas fa-link-alt`
+  View: new (...arg: any[]) => V
+  Ctrl: new (svc: BsFeatureService, data: TFeatureCmpInput) => IRegionalFeatureReadyDirective
+}
+
+@Injectable({
+  providedIn: 'root'
+})
+export class BsFeatureService{
+
+  static SpaceFeatureSet = new Set([
+    IEEG_FEATURE_KEY
+  ])
+
+  public registeredFeatures: TRegisteredFeature[] = []
+  public registeredFeatures$ = new BehaviorSubject<TRegisteredFeature[]>(this.registeredFeatures)
+  public getAllFeatures$ = this.http.get(`${this.bsEndpoint}/features`).pipe(
+    shareReplay(1)
+  )
+
+  public listFeatures(region: TRegion){
+    const { context } = region
+    const { atlas, parcellation } = context
+    return this.http.get<IFeatureList>(
+      `${this.bsEndpoint}/atlases/${encodeURIComponent(atlas["@id"])}/parcellations/${encodeURIComponent(parcellation['@id'])}/regions/${encodeURIComponent(processRegion(region))}/features`
+    )
+  }
+
+  private getUrl(arg: {
+    atlasId: string
+    parcId: string
+    spaceId: string
+    region: TRegion
+    featureName: string
+    featureId?: string
+  }){
+    const { 
+      atlasId,
+      parcId,
+      spaceId,
+      region,
+      featureName,
+      featureId,
+    } = arg
+
+    if (BsFeatureService.SpaceFeatureSet.has(featureName)) {
+      
+      const url = new URL(`${this.bsEndpoint}/atlases/${encodeURIComponent(atlasId)}/spaces/${encodeURIComponent(spaceId)}/features/${encodeURIComponent(featureName)}${ featureId ? ('/' + encodeURIComponent(featureId)) : '' }`)
+      url.searchParams.set('parcellation_id', parcId)
+      url.searchParams.set('region', processRegion(region))
+
+      return url.toString()
+    }
+    
+    if (!featureId) {
+      return `${this.bsEndpoint}/atlases/${encodeURIComponent(atlasId)}/parcellations/${encodeURIComponent(parcId)}/regions/${encodeURIComponent(processRegion(region))}/features/${encodeURIComponent(featureName)}`
+    }
+    return `${this.bsEndpoint}/atlases/${encodeURIComponent(atlasId)}/parcellations/${encodeURIComponent(parcId)}/regions/${encodeURIComponent(processRegion(region))}/features/${encodeURIComponent(featureName)}/${encodeURIComponent(featureId)}`
+  }
+
+  @CachedFunction({
+    serialization: (featureName, region) => `${featureName}::${processRegion(region)}`
+  })
+  public getFeatures<T extends keyof IBSSummaryResponse>(featureName: T, region: TRegion){
+    const { context } = region
+    const { atlas, parcellation, template } = context
+    const url = this.getUrl({
+      atlasId: atlas['@id'],
+      parcId: parcellation['@id'],
+      region,
+      featureName,
+      spaceId: template['@id']
+    })
+    
+    return this.http.get<IBSSummaryResponse[T][]>(
+      url
+    ).pipe(
+      shareReplay(1)
+    )
+  }
+
+  @CachedFunction({
+    serialization: (featureName, region, featureId) => `${featureName}::${processRegion(region)}::${featureId}`
+  })
+  public getFeature<T extends keyof IBSDetailResponse>(featureName: T, region: TRegion, featureId: string) {
+    const { context } = region
+    const { atlas, parcellation, template } = context
+    const url = this.getUrl({
+      atlasId: atlas['@id'],
+      parcId: parcellation['@id'],
+      spaceId: template['@id'],
+      region,
+      featureName,
+      featureId
+    })
+    return this.http.get<IBSSummaryResponse[T]&IBSDetailResponse[T]>(url).pipe(
+      shareReplay(1)
+    )
+  }
+
+  public registerFeature(feature: TRegisteredFeature){
+    if (this.registeredFeatures.find(v => v.name === feature.name)) {
+      throw new Error(`feature ${feature.name} already registered`)
+    }
+    this.registeredFeatures.push(feature)
+    this.registeredFeatures$.next(this.registeredFeatures)
+  }
+
+  public deregisterFeature(name: string){
+    this.registeredFeatures = this.registeredFeatures.filter(v => v.name !== name)
+    this.registeredFeatures$.next(this.registeredFeatures)
+  }
+  
+  constructor(
+    private http: HttpClient,
+    @Inject(BS_ENDPOINT) private bsEndpoint: string,
+  ){
+
+  }
+}
diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/type.ts b/src/atlasComponents/regionalFeatures/bsFeatures/type.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b16d5dcad496634d3a1557780520c9de1312ce7b
--- /dev/null
+++ b/src/atlasComponents/regionalFeatures/bsFeatures/type.ts
@@ -0,0 +1,52 @@
+import { IHasId } from "src/util/interfaces";
+import { TBSDetail as TReceptorDetail, TBSSummary as TReceptorSummary } from "./receptor/type";
+import { KG_REGIONAL_FEATURE_KEY, TBSDetail as TKGDetail, TBSSummary as TKGSummary } from './kgRegionalFeature/type'
+import { SIIBRA_FEATURE_KEY, TBSSummary as TIEEGSummary, TBSDEtail as TIEEGDetail } from './ieeg/type'
+import { Observable } from "rxjs";
+import { InjectionToken } from "@angular/core";
+
+/**
+ * change KgRegionalFeature -> EbrainsRegionalDataset in prod
+ */
+
+export interface IBSSummaryResponse {
+  'ReceptorDistribution': TReceptorSummary
+  [KG_REGIONAL_FEATURE_KEY]: TKGSummary
+  [SIIBRA_FEATURE_KEY]: TIEEGSummary
+}
+
+export interface IBSDetailResponse {
+  'ReceptorDistribution': TReceptorDetail
+  [KG_REGIONAL_FEATURE_KEY]: TKGDetail
+  [SIIBRA_FEATURE_KEY]: TIEEGDetail
+}
+
+export type TRegion = {
+  name: string
+  status?: string
+  context: {
+    atlas: IHasId
+    template: IHasId
+    parcellation: IHasId
+  }
+}
+
+export interface IFeatureList {
+  features: {
+    [key: string]: string
+  }[]
+}
+
+export interface IRegionalFeatureReadyDirective {
+  ngOnDestroy(): void
+  busy$: Observable<boolean>
+  results$: Observable<IBSSummaryResponse[keyof IBSSummaryResponse][]>
+}
+
+export type TContextedFeature<T extends keyof IBSSummaryResponse> = {
+  featureName: string
+  icon: string
+  result: IBSSummaryResponse[T]
+}
+
+export const GENERIC_INFO_INJ_TOKEN = new InjectionToken('GENERIC_INFO_INJ_TOKEN')
diff --git a/src/ui/regionalFeatures/featureContainer/featureContainer.component.spec.ts b/src/atlasComponents/regionalFeatures/featureContainer/featureContainer.component.spec.ts
similarity index 100%
rename from src/ui/regionalFeatures/featureContainer/featureContainer.component.spec.ts
rename to src/atlasComponents/regionalFeatures/featureContainer/featureContainer.component.spec.ts
diff --git a/src/ui/regionalFeatures/featureContainer/featureContainer.component.ts b/src/atlasComponents/regionalFeatures/featureContainer/featureContainer.component.ts
similarity index 89%
rename from src/ui/regionalFeatures/featureContainer/featureContainer.component.ts
rename to src/atlasComponents/regionalFeatures/featureContainer/featureContainer.component.ts
index 6a66668fe1ac2c287ff382026bdbd8f9bc2062df..2b2391599494ac22cadc250fa479255fa39d2179 100644
--- a/src/ui/regionalFeatures/featureContainer/featureContainer.component.ts
+++ b/src/atlasComponents/regionalFeatures/featureContainer/featureContainer.component.ts
@@ -1,4 +1,4 @@
-import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ComponentFactoryResolver, ComponentRef, Input, OnChanges, Output, SimpleChanges, ViewContainerRef, EventEmitter } from "@angular/core";
+import { ChangeDetectionStrategy, Component, ComponentFactoryResolver, ComponentRef, Input, OnChanges, Output, SimpleChanges, ViewContainerRef, EventEmitter } from "@angular/core";
 import { Subscription } from "rxjs";
 import { IFeature, RegionalFeaturesService } from "../regionalFeature.service";
 import { ISingleFeature } from "../singleFeatures/interfaces";
diff --git a/src/ui/regionalFeatures/index.ts b/src/atlasComponents/regionalFeatures/index.ts
similarity index 100%
rename from src/ui/regionalFeatures/index.ts
rename to src/atlasComponents/regionalFeatures/index.ts
diff --git a/src/ui/regionalFeatures/module.ts b/src/atlasComponents/regionalFeatures/module.ts
similarity index 89%
rename from src/ui/regionalFeatures/module.ts
rename to src/atlasComponents/regionalFeatures/module.ts
index f90b3a6ee0593d324d30394b8ba746541f8fa2c0..26b4faf36a3bbad5e17117927da743afa09ee696 100644
--- a/src/ui/regionalFeatures/module.ts
+++ b/src/atlasComponents/regionalFeatures/module.ts
@@ -1,7 +1,8 @@
 import { CommonModule } from "@angular/common";
+import { HttpClientModule } from "@angular/common/http";
 import { NgModule } from "@angular/core";
 import { UtilModule } from "src/util";
-import { AngularMaterialModule } from "../sharedModules/angularMaterial.module";
+import { AngularMaterialModule } from "../../ui/sharedModules/angularMaterial.module";
 import { FeatureContainer } from "./featureContainer/featureContainer.component";
 import { FilterRegionalFeaturesByTypePipe } from "./pipes/filterRegionalFeaturesByType.pipe";
 import { FilterRegionFeaturesById } from "./pipes/filterRegionFeaturesById.pipe";
@@ -18,6 +19,7 @@ import { ReceptorDensityModule } from "./singleFeatures/receptorDensity/module";
     AngularMaterialModule,
     FeatureIEEGRecordings,
     ReceptorDensityModule,
+    HttpClientModule,
   ],
   declarations: [
     /**
diff --git a/src/ui/regionalFeatures/pipes/filterRegionFeaturesById.pipe.ts b/src/atlasComponents/regionalFeatures/pipes/filterRegionFeaturesById.pipe.ts
similarity index 100%
rename from src/ui/regionalFeatures/pipes/filterRegionFeaturesById.pipe.ts
rename to src/atlasComponents/regionalFeatures/pipes/filterRegionFeaturesById.pipe.ts
diff --git a/src/ui/regionalFeatures/pipes/filterRegionalFeaturesByType.pipe.ts b/src/atlasComponents/regionalFeatures/pipes/filterRegionalFeaturesByType.pipe.ts
similarity index 100%
rename from src/ui/regionalFeatures/pipes/filterRegionalFeaturesByType.pipe.ts
rename to src/atlasComponents/regionalFeatures/pipes/filterRegionalFeaturesByType.pipe.ts
diff --git a/src/ui/regionalFeatures/pipes/findRegionFeatureById.pipe.ts b/src/atlasComponents/regionalFeatures/pipes/findRegionFeatureById.pipe.ts
similarity index 100%
rename from src/ui/regionalFeatures/pipes/findRegionFeatureById.pipe.ts
rename to src/atlasComponents/regionalFeatures/pipes/findRegionFeatureById.pipe.ts
diff --git a/src/ui/regionalFeatures/regionGetAllFeatures.directive.ts b/src/atlasComponents/regionalFeatures/regionGetAllFeatures.directive.ts
similarity index 100%
rename from src/ui/regionalFeatures/regionGetAllFeatures.directive.ts
rename to src/atlasComponents/regionalFeatures/regionGetAllFeatures.directive.ts
diff --git a/src/ui/regionalFeatures/regionalFeature.service.ts b/src/atlasComponents/regionalFeatures/regionalFeature.service.ts
similarity index 92%
rename from src/ui/regionalFeatures/regionalFeature.service.ts
rename to src/atlasComponents/regionalFeatures/regionalFeature.service.ts
index 3a4f61c055c98d7d7bdfa139525f36b5241a6929..0525ba80e033d9db9245d54f368b152b210fda11 100644
--- a/src/ui/regionalFeatures/regionalFeature.service.ts
+++ b/src/atlasComponents/regionalFeatures/regionalFeature.service.ts
@@ -65,8 +65,17 @@ export class RegionalFeaturesService implements OnDestroy{
     select(uiStateMouseoverUserLandmark)
   )
 
-  public getAllFeaturesByRegion(region: {['fullId']: string}){
-    if (!region.fullId) return throwError(`getAllFeaturesByRegion - region does not have fullId defined`)
+  public getAllFeaturesByRegion(_region: {['fullId']: string} | { id: { kg: {kgSchema: string, kgId: string} } }){
+    
+    const region = {
+      ..._region,
+    }
+    if (!region['fullId']) {
+      const { kgSchema, kgId } = region['id']?.kg || {}
+      if (kgSchema && kgId) region['fullId'] = `${kgSchema}/${kgId}`
+    }
+
+    if (!region['fullId']) throw new Error(`getAllFeaturesByRegion - region does not have fullId defined`)
     const regionFullIds = getStringIdsFromRegion(region)
     const hemisphereObj = (() => {
       const hemisphere = getRegionHemisphere(region)
diff --git a/src/ui/regionalFeatures/singleFeatures/base/regionFeature.base.ts b/src/atlasComponents/regionalFeatures/singleFeatures/base/regionFeature.base.ts
similarity index 100%
rename from src/ui/regionalFeatures/singleFeatures/base/regionFeature.base.ts
rename to src/atlasComponents/regionalFeatures/singleFeatures/base/regionFeature.base.ts
diff --git a/src/ui/regionalFeatures/singleFeatures/iEEGRecordings/iEEGRecordings/iEEGRecordings.component.ts b/src/atlasComponents/regionalFeatures/singleFeatures/iEEGRecordings/iEEGRecordings/iEEGRecordings.component.ts
similarity index 95%
rename from src/ui/regionalFeatures/singleFeatures/iEEGRecordings/iEEGRecordings/iEEGRecordings.component.ts
rename to src/atlasComponents/regionalFeatures/singleFeatures/iEEGRecordings/iEEGRecordings/iEEGRecordings.component.ts
index 97401c745df17c8ef6409ac6e02869494d406bbe..eb9e3c32e9d0ad71e1cd57c5ab1c33157eeae450 100644
--- a/src/ui/regionalFeatures/singleFeatures/iEEGRecordings/iEEGRecordings/iEEGRecordings.component.ts
+++ b/src/atlasComponents/regionalFeatures/singleFeatures/iEEGRecordings/iEEGRecordings/iEEGRecordings.component.ts
@@ -3,7 +3,7 @@ import { Store } from "@ngrx/store";
 import { merge, Subject, Subscription } from "rxjs";
 import { debounceTime, map, scan, take } from "rxjs/operators";
 import { viewerStateChangeNavigation } from "src/services/state/viewerState/actions";
-import { RegionalFeaturesService } from "src/ui/regionalFeatures/regionalFeature.service";
+import { RegionalFeaturesService } from "src/atlasComponents/regionalFeatures/regionalFeature.service";
 import { ClickInterceptor, CLICK_INTERCEPTOR_INJECTOR } from "src/util";
 import { IHasId } from "src/util/interfaces";
 import { RegionFeatureBase } from "../../base/regionFeature.base";
@@ -150,18 +150,18 @@ export class IEEGRecordingsCmp extends RegionFeatureBase implements ISingleFeatu
     }, [])
   )
 
-  private clickIntp(ev: any, next: Function) {
+  private clickIntp(ev: any): boolean {
     let hoveredLandmark = null
     this.regionFeatureService.onHoverLandmarks$.pipe(
       take(1)
     ).subscribe(val => {
       hoveredLandmark = val
     })
-    if (!hoveredLandmark) return next()
+    if (!hoveredLandmark) return true
     const isOne = this.landmarksLoaded.some(lm => {
       return lm['_']['electrodeId'] === hoveredLandmark['_']['electrodeId']
     })
-    if (!isOne) return next()
+    if (!isOne) return true
     this.exploreElectrode$.next(hoveredLandmark['_']['electrodeId'])
   }
 }
diff --git a/src/ui/regionalFeatures/singleFeatures/iEEGRecordings/iEEGRecordings/iEEGRecordings.style.css b/src/atlasComponents/regionalFeatures/singleFeatures/iEEGRecordings/iEEGRecordings/iEEGRecordings.style.css
similarity index 100%
rename from src/ui/regionalFeatures/singleFeatures/iEEGRecordings/iEEGRecordings/iEEGRecordings.style.css
rename to src/atlasComponents/regionalFeatures/singleFeatures/iEEGRecordings/iEEGRecordings/iEEGRecordings.style.css
diff --git a/src/ui/regionalFeatures/singleFeatures/iEEGRecordings/iEEGRecordings/iEEGRecordings.template.html b/src/atlasComponents/regionalFeatures/singleFeatures/iEEGRecordings/iEEGRecordings/iEEGRecordings.template.html
similarity index 100%
rename from src/ui/regionalFeatures/singleFeatures/iEEGRecordings/iEEGRecordings/iEEGRecordings.template.html
rename to src/atlasComponents/regionalFeatures/singleFeatures/iEEGRecordings/iEEGRecordings/iEEGRecordings.template.html
diff --git a/src/ui/regionalFeatures/singleFeatures/iEEGRecordings/module.ts b/src/atlasComponents/regionalFeatures/singleFeatures/iEEGRecordings/module.ts
similarity index 100%
rename from src/ui/regionalFeatures/singleFeatures/iEEGRecordings/module.ts
rename to src/atlasComponents/regionalFeatures/singleFeatures/iEEGRecordings/module.ts
diff --git a/src/ui/regionalFeatures/singleFeatures/interfaces.ts b/src/atlasComponents/regionalFeatures/singleFeatures/interfaces.ts
similarity index 100%
rename from src/ui/regionalFeatures/singleFeatures/interfaces.ts
rename to src/atlasComponents/regionalFeatures/singleFeatures/interfaces.ts
diff --git a/src/ui/regionalFeatures/singleFeatures/receptorDensity/filterReceptorBytype.pipe.ts b/src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/filterReceptorBytype.pipe.ts
similarity index 100%
rename from src/ui/regionalFeatures/singleFeatures/receptorDensity/filterReceptorBytype.pipe.ts
rename to src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/filterReceptorBytype.pipe.ts
diff --git a/src/ui/regionalFeatures/singleFeatures/receptorDensity/getAllReceptors.pipe.ts b/src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/getAllReceptors.pipe.ts
similarity index 100%
rename from src/ui/regionalFeatures/singleFeatures/receptorDensity/getAllReceptors.pipe.ts
rename to src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/getAllReceptors.pipe.ts
diff --git a/src/ui/regionalFeatures/singleFeatures/receptorDensity/getId.pipe.ts b/src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/getId.pipe.ts
similarity index 100%
rename from src/ui/regionalFeatures/singleFeatures/receptorDensity/getId.pipe.ts
rename to src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/getId.pipe.ts
diff --git a/src/ui/regionalFeatures/singleFeatures/receptorDensity/getUrl.pipe.ts b/src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/getUrl.pipe.ts
similarity index 100%
rename from src/ui/regionalFeatures/singleFeatures/receptorDensity/getUrl.pipe.ts
rename to src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/getUrl.pipe.ts
diff --git a/src/ui/regionalFeatures/singleFeatures/receptorDensity/module.ts b/src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/module.ts
similarity index 100%
rename from src/ui/regionalFeatures/singleFeatures/receptorDensity/module.ts
rename to src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/module.ts
diff --git a/src/ui/regionalFeatures/singleFeatures/receptorDensity/receptorDensity/receptorDensity.component.ts b/src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/receptorDensity/receptorDensity.component.ts
similarity index 95%
rename from src/ui/regionalFeatures/singleFeatures/receptorDensity/receptorDensity/receptorDensity.component.ts
rename to src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/receptorDensity/receptorDensity.component.ts
index 1ecf5f8d6c8b1f60bcb2520039666fe0b4a3f17d..a9b5f3652f04a53961f3a334f7a951ba36fa5407 100644
--- a/src/ui/regionalFeatures/singleFeatures/receptorDensity/receptorDensity/receptorDensity.component.ts
+++ b/src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/receptorDensity/receptorDensity.component.ts
@@ -1,6 +1,6 @@
 import { Component, ElementRef, EventEmitter, HostListener, OnDestroy, Optional } from "@angular/core";
 import { fromEvent, Observable, of, Subscription } from "rxjs";
-import { RegionalFeaturesService } from "src/ui/regionalFeatures/regionalFeature.service";
+import { RegionalFeaturesService } from "src/atlasComponents/regionalFeatures/regionalFeature.service";
 import { PureContantService } from "src/util";
 import { RegionFeatureBase } from "../../base/regionFeature.base";
 import { ISingleFeature } from "../../interfaces";
diff --git a/src/ui/regionalFeatures/singleFeatures/receptorDensity/receptorDensity/receptorDensity.style.css b/src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/receptorDensity/receptorDensity.style.css
similarity index 100%
rename from src/ui/regionalFeatures/singleFeatures/receptorDensity/receptorDensity/receptorDensity.style.css
rename to src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/receptorDensity/receptorDensity.style.css
diff --git a/src/ui/regionalFeatures/singleFeatures/receptorDensity/receptorDensity/receptorDensity.template.html b/src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/receptorDensity/receptorDensity.template.html
similarity index 100%
rename from src/ui/regionalFeatures/singleFeatures/receptorDensity/receptorDensity/receptorDensity.template.html
rename to src/atlasComponents/regionalFeatures/singleFeatures/receptorDensity/receptorDensity/receptorDensity.template.html
diff --git a/src/ui/regionalFeatures/util.ts b/src/atlasComponents/regionalFeatures/util.ts
similarity index 100%
rename from src/ui/regionalFeatures/util.ts
rename to src/atlasComponents/regionalFeatures/util.ts
diff --git a/src/atlasComponents/splashScreen/index.ts b/src/atlasComponents/splashScreen/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5927a7b64a7f19529a4998770dd5f27a3d3a2546
--- /dev/null
+++ b/src/atlasComponents/splashScreen/index.ts
@@ -0,0 +1,2 @@
+export { GetTemplateImageSrcPipe, SplashScreen } from "./splashScreen/splashScreen.component";
+export { SplashUiModule } from './module'
\ No newline at end of file
diff --git a/src/atlasComponents/splashScreen/module.ts b/src/atlasComponents/splashScreen/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..28f361cd4c80a751efa5d0e719fc37073faee18d
--- /dev/null
+++ b/src/atlasComponents/splashScreen/module.ts
@@ -0,0 +1,26 @@
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { ComponentsModule } from "src/components";
+import { KgTosModule } from "src/ui/kgtos/module";
+import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module";
+import { UtilModule } from "src/util";
+import { GetTemplateImageSrcPipe, SplashScreen } from "./splashScreen/splashScreen.component";
+
+@NgModule({
+  imports: [
+    AngularMaterialModule,
+    CommonModule,
+    UtilModule,
+    KgTosModule,
+    ComponentsModule,
+  ],
+  declarations: [
+    SplashScreen,
+    GetTemplateImageSrcPipe,
+  ],
+  exports: [
+    SplashScreen,
+  ]
+})
+
+export class SplashUiModule{}
\ No newline at end of file
diff --git a/src/atlasComponents/splashScreen/splashScreen/splashScreen.component.ts b/src/atlasComponents/splashScreen/splashScreen/splashScreen.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2435c4e089495a84549374d29f77e1bce8bec238
--- /dev/null
+++ b/src/atlasComponents/splashScreen/splashScreen/splashScreen.component.ts
@@ -0,0 +1,66 @@
+import { Component, ElementRef, Pipe, PipeTransform, ViewChild } from "@angular/core";
+import { MatSnackBar } from "@angular/material/snack-bar";
+import { select, Store } from "@ngrx/store";
+import { Observable, Subject, Subscription } from "rxjs";
+import { filter } from 'rxjs/operators'
+import { viewerStateHelperStoreName, viewerStateSelectAtlas } from "src/services/state/viewerState.store.helper";
+import { PureContantService } from "src/util";
+import { CONST } from 'common/constants'
+
+@Component({
+  selector : 'ui-splashscreen',
+  templateUrl : './splashScreen.template.html',
+  styleUrls : [
+    `./splashScreen.style.css`,
+  ],
+})
+
+export class SplashScreen {
+
+  public finishedLoading: boolean = false
+
+  public loadedAtlases$: Observable<any[]>
+
+  @ViewChild('parentContainer', {read: ElementRef})
+  public activatedTemplate$: Subject<any> = new Subject()
+
+  private subscriptions: Subscription[] = []
+
+  constructor(
+    private store: Store<any>,
+    private snack: MatSnackBar,
+    private pureConstantService: PureContantService
+  ) {
+    this.subscriptions.push(
+      this.pureConstantService.allFetchingReady$.subscribe(flag => this.finishedLoading = flag)
+    )
+
+    this.loadedAtlases$ = this.store.pipe(
+      select(state => state[viewerStateHelperStoreName]),
+      select(state => state.fetchedAtlases),
+      filter(v => !!v)
+    )
+  }
+
+  public selectAtlas(atlas: any){
+    if (!this.finishedLoading) {
+      this.snack.open(CONST.DATA_NOT_READY, null, {
+        duration: 3000
+      })
+      return
+    }
+    this.store.dispatch(
+      viewerStateSelectAtlas({ atlas })
+    )
+  }
+}
+
+@Pipe({
+  name: 'getTemplateImageSrcPipe',
+})
+
+export class GetTemplateImageSrcPipe implements PipeTransform {
+  public transform(name: string): string {
+    return `./res/image/${name.replace(/[|&;$%@()+,\s./]/g, '')}.png`
+  }
+}
diff --git a/src/ui/nehubaContainer/splashScreen/splashScreen.style.css b/src/atlasComponents/splashScreen/splashScreen/splashScreen.style.css
similarity index 100%
rename from src/ui/nehubaContainer/splashScreen/splashScreen.style.css
rename to src/atlasComponents/splashScreen/splashScreen/splashScreen.style.css
diff --git a/src/ui/nehubaContainer/splashScreen/splashScreen.template.html b/src/atlasComponents/splashScreen/splashScreen/splashScreen.template.html
similarity index 76%
rename from src/ui/nehubaContainer/splashScreen/splashScreen.template.html
rename to src/atlasComponents/splashScreen/splashScreen/splashScreen.template.html
index 41922dbfd31ffc03218ae3c13ca87c7af726ce82..bb855b2ef8cf1226bc3793b664644e14d902250b 100644
--- a/src/ui/nehubaContainer/splashScreen/splashScreen.template.html
+++ b/src/atlasComponents/splashScreen/splashScreen/splashScreen.template.html
@@ -5,7 +5,7 @@
     #parentContainer
     class="p-5 w-100 d-flex flex-column flex-wrap justify-content-center align-items-stretch pe-all">
     <mat-card
-      (mousedown)="activatedTemplate$.next(atlas)"
+      (click)="selectAtlas(atlas)"
       matRipple
       *ngFor="let atlas of loadedAtlases$ | async | filterNull"
       class="m-3 col-md-12 col-lg-12 pe-all">
@@ -20,12 +20,13 @@
       <mat-card-footer>
       </mat-card-footer>
     </mat-card>
-    <ng-container *ngIf="stillLoadingAtlases$ | async as stillLoadingTemplates">
-      <div class="d-flex align-items-center p-4 loadingIndicator" *ngFor="let t of stillLoadingTemplates">
+
+    <ng-template [ngIf]="!finishedLoading">
+      <div class="d-flex align-items-center p-4">
         <h1 class="mat-h1">
           <spinner-cmp></spinner-cmp>
         </h1>
       </div>
-    </ng-container>
+    </ng-template>
   </div>
 </div>
\ No newline at end of file
diff --git a/src/ui/atlasDropdown/atlasDropdown.component.spec.ts b/src/atlasComponents/uiSelectors/atlasDropdown/atlasDropdown.component.spec.ts
similarity index 100%
rename from src/ui/atlasDropdown/atlasDropdown.component.spec.ts
rename to src/atlasComponents/uiSelectors/atlasDropdown/atlasDropdown.component.spec.ts
diff --git a/src/ui/atlasDropdown/atlasDropdown.component.ts b/src/atlasComponents/uiSelectors/atlasDropdown/atlasDropdown.component.ts
similarity index 100%
rename from src/ui/atlasDropdown/atlasDropdown.component.ts
rename to src/atlasComponents/uiSelectors/atlasDropdown/atlasDropdown.component.ts
diff --git a/src/ui/kgtos/kgtos.style.css b/src/atlasComponents/uiSelectors/atlasDropdown/atlasDropdown.style.css
similarity index 100%
rename from src/ui/kgtos/kgtos.style.css
rename to src/atlasComponents/uiSelectors/atlasDropdown/atlasDropdown.style.css
diff --git a/src/ui/atlasDropdown/atlasDropdown.template.html b/src/atlasComponents/uiSelectors/atlasDropdown/atlasDropdown.template.html
similarity index 100%
rename from src/ui/atlasDropdown/atlasDropdown.template.html
rename to src/atlasComponents/uiSelectors/atlasDropdown/atlasDropdown.template.html
diff --git a/src/atlasComponents/uiSelectors/atlasLayerSelector/atlasLayerSelector.component.ts b/src/atlasComponents/uiSelectors/atlasLayerSelector/atlasLayerSelector.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..dba91ed89252484d6d40c8f14e0baf30241d4f1c
--- /dev/null
+++ b/src/atlasComponents/uiSelectors/atlasLayerSelector/atlasLayerSelector.component.ts
@@ -0,0 +1,334 @@
+import { Component, OnInit, ViewChildren, QueryList, HostBinding, ViewChild, TemplateRef, ElementRef, Pipe, PipeTransform } from "@angular/core";
+import { select, Store } from "@ngrx/store";
+import { distinctUntilChanged, map, withLatestFrom, shareReplay, groupBy, mergeMap, toArray, switchMap, scan, filter, tap } from "rxjs/operators";
+import { Observable, Subscription, from, zip, of, combineLatest } from "rxjs";
+import { viewerStateSelectTemplateWithId, viewerStateToggleLayer } from "src/services/state/viewerState.store.helper";
+import { MatMenuTrigger } from "@angular/material/menu";
+import { viewerStateGetSelectedAtlas, viewerStateAtlasLatestParcellationSelector, viewerStateSelectedTemplateFullInfoSelector, viewerStateSelectedTemplatePureSelector, viewerStateSelectedParcellationSelector } from "src/services/state/viewerState/selectors";
+import { ARIA_LABELS, QUICKTOUR_DESC } from 'common/constants'
+import { IQuickTourData } from "src/ui/quickTour/constrants";
+import { animate, state, style, transition, trigger } from "@angular/animations";
+
+@Component({
+  selector: 'atlas-layer-selector',
+  templateUrl: './atlasLayerSelector.template.html',
+  styleUrls: ['./atlasLayerSelector.style.css'],
+  exportAs: 'atlasLayerSelector',
+  animations: [
+    trigger('toggleAtlasLayerSelector', [
+      state('false', style({
+        transform: 'scale(0)',
+        opacity: 0,
+        transformOrigin: '0% 100%'
+      })),
+      state('true', style({
+        transform: 'scale(1)',
+        opacity: 1,
+        transformOrigin: '0% 100%'
+      })),
+      transition('false => true', [
+        animate('200ms cubic-bezier(0.35, 0, 0.25, 1)')
+      ]),
+      transition('true => false', [
+        animate('200ms cubic-bezier(0.35, 0, 0.25, 1)')
+      ])
+    ])
+  ],
+  providers:[
+    {
+      provide: OVERWRITE_SHOW_DATASET_DIALOG_TOKEN,
+      useValue: null
+    }
+  ]
+})
+export class AtlasLayerSelector implements OnInit {
+
+    public TOGGLE_ATLAS_LAYER_SELECTOR = ARIA_LABELS.TOGGLE_ATLAS_LAYER_SELECTOR
+
+    @ViewChildren(MatMenuTrigger) matMenuTriggers: QueryList<MatMenuTrigger>
+    public atlas: any
+
+    @ViewChild('selectorPanelTmpl', { read: ElementRef })
+    selectorPanelTemplateRef: ElementRef
+
+    public selectedAtlas$: Observable<any> = this.store$.pipe(
+      select(viewerStateGetSelectedAtlas),
+      distinctUntilChanged(),
+      shareReplay(1)
+    )
+    private layersGroupBy$ = this.selectedAtlas$.pipe(
+      switchMap(selectedAtlas => from((selectedAtlas?.parcellations) || []).pipe(
+        /**
+         * do not show base layers
+         */
+        filter(p => !(p as any).baseLayer),
+        groupBy((parcellation: any) => parcellation.groupName, p => p),
+        mergeMap(group => zip(
+          of(group.key),
+          group.pipe(toArray()))
+        ),
+        scan((acc, curr) => acc.concat([ curr ]), []),
+        shareReplay(1),
+      ))
+    )
+
+
+    private atlasLayersLatest$ = this.store$.pipe(
+      select(viewerStateAtlasLatestParcellationSelector),
+      shareReplay(1),
+    )
+
+    public nonGroupedLayers$: Observable<any[]> = this.atlasLayersLatest$.pipe(
+      map(allParcellations =>
+        allParcellations
+          .filter(p => !p['groupName'])
+          .filter(p => !p['baseLayer'])
+      ),
+    )
+
+    public groupedLayers$: Observable<any[]> = combineLatest([
+      this.atlasLayersLatest$.pipe(
+        map(allParcellations =>
+          allParcellations.filter(p => !p['baseLayer'])
+        ),
+      ),
+      this.layersGroupBy$
+    ]).pipe(
+      map(([ allParcellations, arr]) => arr
+        .filter(([ key ]) => !!key )
+        .map(([key, parcellations]) => ({
+          name: key,
+          previewUrl: parcellations[0].previewUrl,
+          parcellations: parcellations.map(p => {
+            const fullInfo = allParcellations.find(fullP => fullP['@id'] === p['@id']) || {}
+            return {
+              ...fullInfo,
+              ...p,
+              darktheme: (fullInfo || {}).useTheme === 'dark'
+            }
+          })
+        }))
+      ),
+    )
+    public selectedTemplateSpaceId: string
+    public selectedLayers = []
+
+    public selectedTemplate$: Observable<any>
+    private selectedParcellation$: Observable<any>
+
+    private subscriptions: Subscription[] = []
+
+    @HostBinding('attr.data-opened')
+    public selectorExpanded: boolean = false
+    public selectedTemplatePreviewUrl: string = ''
+        
+    public quickTourData: IQuickTourData = {
+      order: 4,
+      description: QUICKTOUR_DESC.LAYER_SELECTOR,
+    }
+
+    public availableTemplates$ = this.store$.pipe<any[]>(
+      select(viewerStateSelectedTemplateFullInfoSelector),
+    )
+
+    public containerMaxWidth: number
+
+    public shouldShowRenderPlaceHolder$ = combineLatest([
+      this.availableTemplates$,
+      this.groupedLayers$,
+      this.nonGroupedLayers$,
+    ]).pipe(
+      map(([ availTmpl, grpL, ungrpL ]) => {
+        return availTmpl?.length > 0 || (grpL?.length || 0) + (ungrpL?.length || 0) > 0
+      })
+    )
+
+    constructor(private store$: Store<any>) {
+
+      this.selectedTemplate$ = this.store$.pipe(
+        select(viewerStateSelectedTemplatePureSelector),
+        withLatestFrom(this.selectedAtlas$),
+        map(([templateSelected, templateFromAtlas]) => {
+          return {
+            ...templateFromAtlas,
+            ...templateSelected
+          }
+        })
+      )
+      this.selectedParcellation$ = this.store$.pipe(
+        select(viewerStateSelectedParcellationSelector)
+      )
+
+    }
+
+    ngOnInit(): void {
+      this.subscriptions.push(
+        this.selectedTemplate$.subscribe(st => {
+          this.selectedTemplatePreviewUrl = st.templateSpaces?.find(t => t['@id'] === st['@id']).previewUrl
+          this.selectedTemplateSpaceId = st['@id']
+        }),
+      )
+      this.subscriptions.push(
+        this.selectedParcellation$.subscribe(ps => {
+          this.selectedLayers = (this.atlas && [this.atlas.parcellations.find(l => l['@id'] === ps['@id'])['@id']]) || []
+        })
+      )
+      this.subscriptions.push(
+        this.selectedAtlas$.subscribe(sa => {
+          this.atlas = sa
+        })
+      )
+    }
+
+    toggleSelector() {
+      this.selectorExpanded = !this.selectorExpanded
+    }
+
+    selectTemplateWithName(template) {
+      this.store$.dispatch(
+        viewerStateSelectTemplateWithId({ payload: template })
+      )
+    }
+
+    selectParcellationWithName(layer) {
+      const templateChangeRequired = !this.currentTemplateIncludesLayer(layer)
+      if (!templateChangeRequired) {
+        this.store$.dispatch(
+          viewerStateToggleLayer({ payload: layer })
+        )
+      } else {
+        this.store$.dispatch(
+          viewerStateSelectTemplateWithId({ payload: layer.availableIn[0], config: { selectParcellation: layer } })
+        )
+      }
+    }
+
+    currentTemplateIncludesLayer(layer) {
+      return layer && layer.availableIn.map(a => a['@id']).includes(this.selectedTemplateSpaceId)
+    }
+
+    templateIncludesGroup(group) {
+      return group.parcellations.some(v => v.availableIn.map(t => t['@id']).includes(this.selectedTemplateSpaceId))
+    }
+
+    selectedOneOfTheLayers(layers) {
+      const includes = layers.map(l=>l['@id']).some(id=> this.selectedLayers.includes(id))
+      return includes
+    }
+
+    selectedLayersIncludes(id) {
+      return this.selectedLayers.includes(id)
+    }
+
+    collapseExpandedGroup(){
+      this.matMenuTriggers.forEach(t => t.menuOpen && t.closeMenu())
+    }
+
+    getTileTmplClickFnAsCtx(fn: (...arg) => void, param: any) {
+      return () => fn.call(this, param)
+    }
+
+    getTooltipText(layer) {
+      if (!this.atlas) return
+      if (this.atlas.templateSpaces.map(tmpl => tmpl['@id']).includes(layer['@id'])) return layer.name
+      if (layer.availableIn) {
+        if (this.currentTemplateIncludesLayer(layer)) return layer.name
+        else {
+          const firstAvailableRefSpaceId = layer && Array.isArray(layer.availableIn) && layer.availableIn.length > 0 && layer.availableIn[0]['@id']
+          const firstAvailableRefSpace = firstAvailableRefSpaceId && this.atlas.templateSpaces.find(t => t['@id'] === firstAvailableRefSpaceId)
+          return `${layer.name} 🔄 ${(firstAvailableRefSpace && firstAvailableRefSpace.name) || ''}`
+        }
+      }
+
+      if (layer.parcellations) {
+        if (this.templateIncludesGroup(layer)) return layer.name
+        else return `${layer.name} 🔄`
+      }
+
+      return layer.name
+    }
+
+    trackbyAtId(t){
+      return t['@id']
+    }
+
+    trackbyName(t) {
+      return t['name']
+    }
+}
+
+import "src/res/images/atlas-selection/bigbrain.png"
+import 'src/res/images/atlas-selection/icbm2009c.png'
+import 'src/res/images/atlas-selection/colin27.png'
+import 'src/res/images/atlas-selection/cytoarchitectonic-maps.png'
+import 'src/res/images/atlas-selection/cortical-layers.png'
+import 'src/res/images/atlas-selection/grey-white-matter.png'
+import 'src/res/images/atlas-selection/firbe-long.png'
+import 'src/res/images/atlas-selection/firbe-short.png'
+import 'src/res/images/atlas-selection/difumo-64.png'
+import 'src/res/images/atlas-selection/difumo-128.png'
+import 'src/res/images/atlas-selection/difumo-256.png'
+import 'src/res/images/atlas-selection/difumo-512.png'
+import 'src/res/images/atlas-selection/difumo-1024.png'
+import 'src/res/images/atlas-selection/allen-mouse.png'
+import 'src/res/images/atlas-selection/allen-mouse-2017.png'
+import 'src/res/images/atlas-selection/allen-mouse-2015.png'
+import 'src/res/images/atlas-selection/waxholm.png'
+import 'src/res/images/atlas-selection/waxholm-v3.png'
+import 'src/res/images/atlas-selection/waxholm-v2.png'
+import 'src/res/images/atlas-selection/waxholm-v1.png'
+import 'src/res/images/atlas-selection/short-bundle-hcp.png'
+import 'src/res/images/atlas-selection/freesurfer.png'
+import { OVERWRITE_SHOW_DATASET_DIALOG_TOKEN } from "src/util/interfaces";
+
+const previewImgMap = new Map([
+  ['minds/core/referencespace/v1.0.0/a1655b99-82f1-420f-a3c2-fe80fd4c8588', 'bigbrain.png'],
+  ['minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2', 'icbm2009c.png'],
+  ['minds/core/referencespace/v1.0.0/7f39f7be-445b-47c0-9791-e971c0b6d992', 'colin27.png'],
+  ['minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579', 'cytoarchitectonic-maps.png'],
+  ['juelich/iav/atlas/v1.0.0/3', 'cortical-layers.png'],
+  ['juelich/iav/atlas/v1.0.0/4', 'grey-white-matter.png'],
+  ['juelich/iav/atlas/v1.0.0/5', 'firbe-long.png'],
+  ['juelich/iav/atlas/v1.0.0/6', 'firbe-short.png'],
+  ['minds/core/parcellationatlas/v1.0.0/d80fbab2-ce7f-4901-a3a2-3c8ef8a3b721', 'difumo-64.png'],
+  ['minds/core/parcellationatlas/v1.0.0/73f41e04-b7ee-4301-a828-4b298ad05ab8', 'difumo-128.png'],
+  ['minds/core/parcellationatlas/v1.0.0/141d510f-0342-4f94-ace7-c97d5f160235', 'difumo-256.png'],
+  ['minds/core/parcellationatlas/v1.0.0/63b5794f-79a4-4464-8dc1-b32e170f3d16', 'difumo-512.png'],
+  ['minds/core/parcellationatlas/v1.0.0/12fca5c5-b02c-46ce-ab9f-f12babf4c7e1', 'difumo-1024.png'],
+  ['minds/core/referencespace/v1.0.0/265d32a0-3d84-40a5-926f-bf89f68212b9', 'allen-mouse.png'],
+  ['minds/core/parcellationatlas/v1.0.0/05655b58-3b6f-49db-b285-64b5a0276f83', 'allen-mouse-2017.png'],
+  ['minds/core/parcellationatlas/v1.0.0/39a1384b-8413-4d27-af8d-22432225401f', 'allen-mouse-2015.png'],
+  ['minds/core/referencespace/v1.0.0/d5717c4a-0fa1-46e6-918c-b8003069ade8', 'waxholm.png'],
+  ['minds/core/parcellationatlas/v1.0.0/ebb923ba-b4d5-4b82-8088-fa9215c2e1fe', 'waxholm-v3.png'],
+  ['minds/core/parcellationatlas/v1.0.0/2449a7f0-6dd0-4b5a-8f1e-aec0db03679d', 'waxholm-v2.png'],
+  ['minds/core/parcellationatlas/v1.0.0/11017b35-7056-4593-baad-3934d211daba', 'waxholm-v1.png'],
+  ['juelich/iav/atlas/v1.0.0/79cbeaa4ee96d5d3dfe2876e9f74b3dc3d3ffb84304fb9b965b1776563a1069c', 'short-bundle-hcp.png'],
+  ['minds/core/referencespace/v1.0.0/tmp-fsaverage', 'freesurfer.png'],
+  ['minds/core/referencespace/v1.0.0/tmp-fsaverage6', 'freesurfer.png'],
+  ['minds/core/referencespace/v1.0.0/tmp-hcp32k', 'freesurfer.png'],
+])
+
+/**
+ * used for directories
+ */
+const previewNameToPngMap = new Map([
+  ['fibre architecture', 'firbe-long.png'],
+  ['functional modes', 'difumo-128.png']
+])
+
+@Pipe({
+  name: 'getPreviewUrlPipe',
+  pure: true
+})
+
+export class GetPreviewUrlPipe implements PipeTransform{
+  public transform(tile: any){
+    const filename = tile['@id']
+      ? previewImgMap.get(tile['@id'])
+      : previewNameToPngMap.get(tile['name'])
+    if (!filename) {
+      console.log(tile)
+    }
+    return filename && `res/image/${filename}`
+  }
+}
\ No newline at end of file
diff --git a/src/atlasComponents/uiSelectors/atlasLayerSelector/atlasLayerSelector.style.css b/src/atlasComponents/uiSelectors/atlasLayerSelector/atlasLayerSelector.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..9d43dc845dcf18755d5326699abaf0ce4139011b
--- /dev/null
+++ b/src/atlasComponents/uiSelectors/atlasLayerSelector/atlasLayerSelector.style.css
@@ -0,0 +1,57 @@
+.singleLayerImageContainer img {
+    flex-shrink: 0;
+    width: 70px;
+    height: 70px;
+    border-radius: 10px;
+    -webkit-border-radius: 10px;
+    -moz-border-radius: 10px;
+}
+
+
+.selectedTemplateDefaultContainer {
+    width: 100px;
+    height: 100px;
+    border-radius: 10px;
+}
+.selectedTemplateDefaultContainer img {
+    border-radius: 10px;
+    min-width: 100%;
+    min-height: 100%;
+    width: 100%;
+}
+
+.selectedLayerBorder {
+    border: 2px solid #FED363;
+}
+
+.folder-container
+{
+    margin-right:0.5rem;
+    margin-bottom:-0.5rem;
+}
+
+.info-container
+{
+    margin-right:-0.5rem;
+    margin-top:-0.25rem;
+}
+
+.selector-container
+{
+    width: 21rem;
+    bottom: 4rem;
+    z-index: 5;
+}
+
+.single-column-tile
+{
+    width: calc((28rem - 32px)/3);
+}
+
+.infoButton {
+    height: 18px;
+    width: 18px;
+    margin-top: 10px;
+    margin-right: 12px;
+    border-radius: 50%;
+}
diff --git a/src/ui/atlasLayerSelector/atlasLayerSelector.template.html b/src/atlasComponents/uiSelectors/atlasLayerSelector/atlasLayerSelector.template.html
similarity index 69%
rename from src/ui/atlasLayerSelector/atlasLayerSelector.template.html
rename to src/atlasComponents/uiSelectors/atlasLayerSelector/atlasLayerSelector.template.html
index a4e8883b6769fce8f473aa2bd9b4ced768425114..d4af6f260fb03dc5335ff2dc283c5976b5c13880 100644
--- a/src/ui/atlasLayerSelector/atlasLayerSelector.template.html
+++ b/src/atlasComponents/uiSelectors/atlasLayerSelector/atlasLayerSelector.template.html
@@ -2,7 +2,11 @@
 
     <!-- selector panel when expanded -->
 
-    <mat-card class="selector-container position-absolute" [ngClass]="selectorExpanded ? 'scale-up-bl pe-all' : 'scale-down-bl pe-none'">
+    <mat-card class="selector-container position-absolute"
+        [ngClass]="{'pe-all': selectorExpanded}"
+        [@toggleAtlasLayerSelector]="selectorExpanded"
+        (@toggleAtlasLayerSelector.done)="atlasSelectorTour?.attachTo(selectorExpanded ? selectorPanelTemplateRef : null)"
+        #selectorPanelTmpl>
         <mat-card-content>
 
             <!-- templates -->
@@ -11,7 +15,8 @@
             </mat-card-subtitle>
             <mat-grid-list cols="3" rowHeight="3:4">
                 <!-- template tiles -->
-                <mat-grid-tile *ngFor="let template of availableTemplates$ | async; trackBy: trackbyAtId">
+                <mat-grid-tile *ngFor="let template of availableTemplates$ | async; trackBy: trackbyAtId"
+                    [attr.aria-checked]="selectedTemplateSpaceId === template['@id']">
                     <ng-container *ngTemplateOutlet="tileTmpl; context: {
                         tileSrc: template,
                         selected: selectedTemplateSpaceId === template['@id'],
@@ -31,7 +36,8 @@
             <mat-grid-list cols="3" rowHeight="3:4">
 
                 <!-- non grouped layers -->
-                <mat-grid-tile *ngFor="let layer of (nonGroupedLayers$ | async); trackBy: trackbyAtId">
+                <mat-grid-tile *ngFor="let layer of (nonGroupedLayers$ | async); trackBy: trackbyAtId"
+                    [attr.aria-checked]="selectedLayersIncludes(layer['@id'])">
                     <ng-container *ngTemplateOutlet="tileTmpl; context: {
                         tileSrc: layer,
                         selected: selectedLayersIncludes(layer['@id']),
@@ -43,7 +49,8 @@
                 </mat-grid-tile>
 
                 <!-- grouped layers -->
-                <mat-grid-tile *ngFor="let group of (groupedLayers$ | async); trackBy: trackbyName">
+                <mat-grid-tile *ngFor="let group of (groupedLayers$ | async); trackBy: trackbyName"
+                    [attr.aria-checked]="selectedOneOfTheLayers(group.parcellations)">
                     <ng-container *ngTemplateOutlet="tileTmpl; context: {
                         tileSrc: group,
                         selected: selectedOneOfTheLayers(group.parcellations),
@@ -60,13 +67,17 @@
     </mat-card>
 
     <!-- place holder when not expanded -->
-    <div class="position-relative m-2 cursor-pointer scale-up-bl pe-all">
+    <div class="position-relative m-2 cursor-pointer scale-up-bl pe-all"
+        quick-tour
+        [quick-tour-description]="quickTourData.description"
+        [quick-tour-order]="quickTourData.order"
+        #atlasSelectorTour="quickTour">
         <button color="primary"
-                matTooltip="Select layer"
-                mat-mini-fab
-                *ngIf="((availableTemplates$ | async).length > 1) || ((groupedLayers$ | async).length + (nonGroupedLayers$ | async).length > 1)"
-                [attr.aria-label]="TOGGLE_ATLAS_LAYER_SELECTOR"
-                (click)="selectorExpanded = !selectorExpanded">
+            matTooltip="Select layer"
+            mat-mini-fab
+            *ngIf="shouldShowRenderPlaceHolder$ | async"
+            [attr.aria-label]="TOGGLE_ATLAS_LAYER_SELECTOR"
+            (click)="toggleSelector()">
             <i class="fas fa-layer-group"></i>
         </button>
     </div>
@@ -108,25 +119,25 @@
 
             <div class="position-relative d-flex flex-column align-items-center">
                 <div class="position-relative">
-                <!-- info icon btn -->
-                <div class="position-absolute top-0 right-0 info-container"
-                    [ngClass]="{ 'darktheme': tileSrc.darktheme, 'lighttheme': !tileSrc.darktheme }">
-                    <ng-container *ngTemplateOutlet="infoBtn; context: { tileSrc: tileSrc}">
-
-                    </ng-container>
-                </div>
-
-                <!-- preview image -->
-                <img [src]="tileSrc.previewUrl"
-                    alt="Preview of this tile"
-                    class="layer-image align-self-center"
-                    [ngClass]="{ 'selectedLayerBorder': selected }"
-                    draggable="false">
-
-                <!-- if is directory, show directory icon -->
-                <div *ngIf="isDirectory" class="position-absolute bottom-0 right-0">
-                    <i class="fas fa-folder folder-container fa-2x"></i>
-                </div>
+                    <!-- info icon btn -->
+                    <div class="position-absolute top-0 right-0 info-container"
+                        [ngClass]="{ 'darktheme': tileSrc.darktheme, 'lighttheme': !tileSrc.darktheme }">
+                        <ng-container *ngTemplateOutlet="infoBtn; context: { tileSrc: tileSrc}">
+
+                        </ng-container>
+                    </div>
+
+                    <!-- preview image -->
+                    <img [src]="tileSrc | getPreviewUrlPipe"
+                        alt="Preview of this tile"
+                        class="layer-image align-self-center"
+                        [ngClass]="{ 'selectedLayerBorder': selected }"
+                        draggable="false">
+
+                    <!-- if is directory, show directory icon -->
+                    <div *ngIf="isDirectory" class="position-absolute bottom-0 right-0">
+                        <i class="fas fa-folder folder-container fa-2x"></i>
+                    </div>
                 </div>
             </div>
 
@@ -139,29 +150,20 @@
 </ng-template>
 
 <ng-template #infoBtn let-tileSrc="tileSrc">
-    <div *ngIf="tileSrc.originDatasets?.length > 0; else plainBtn"
-        mat-icon-button
-        iav-stop="click"
-        class="iv-custom-comp d-flex justify-content-center infoButton"
-        [ngStyle]="{backgroundColor: tileSrc.darktheme ? 'white': 'black', color: tileSrc.darktheme ? 'black': 'white' }"
-        iav-dataset-show-dataset-dialog
-        [iav-dataset-show-dataset-dialog-kgid]="tileSrc.originDatasets[0].kgId"
-        [iav-dataset-show-dataset-dialog-kgschema]="tileSrc.originDatasets[0].kgSchema">
-        <small><i class="fas fa-info"></i></small>
-    </div>
+    <ng-container *ngFor="let originDatainfo of tileSrc.originDatainfos">
 
-    <ng-template #plainBtn>
-        <div *ngIf="tileSrc.properties?.name || tileSrc.properties?.description"
-            mat-icon-button
+        <div mat-icon-button
             iav-stop="click"
             class="iv-custom-comp d-flex justify-content-center align-items-center infoButton"
             [ngStyle]="{backgroundColor: tileSrc.darktheme ? 'white': 'black', color: tileSrc.darktheme ? 'black': 'white' }"
             iav-dataset-show-dataset-dialog
-            [iav-dataset-show-dataset-dialog-name]="tileSrc.properties.name"
-            [iav-dataset-show-dataset-dialog-description]="tileSrc.properties.description">
+            [iav-dataset-show-dataset-dialog-name]="originDatainfo.name"
+            [iav-dataset-show-dataset-dialog-description]="originDatainfo.description"
+            [iav-dataset-show-dataset-dialog-urls]="originDatainfo.urls">
             <small><i class="fas fa-info"></i></small>
         </div>
-    </ng-template>
+    </ng-container>
+
 </ng-template>
 
 <!-- mat menu for grouped layer -->
@@ -175,7 +177,8 @@
             rowHeight="1:1"
             iav-stop="click"
             (iav-outsideClick)="collapseExpandedGroup()">
-            <mat-grid-tile *ngFor="let layer of layerGroupItems">
+            <mat-grid-tile *ngFor="let layer of layerGroupItems"
+                [attr.aria-checked]="selectedLayersIncludes(layer['@id'])">
 
                 <ng-container *ngTemplateOutlet="tileTmpl; context: {
                     tileSrc: layer,
diff --git a/src/atlasComponents/uiSelectors/index.ts b/src/atlasComponents/uiSelectors/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..28925e7dac73fcc4f6b8896a1026ec6c556a24e4
--- /dev/null
+++ b/src/atlasComponents/uiSelectors/index.ts
@@ -0,0 +1,3 @@
+export { AtlasCmpUiSelectorsModule } from "./module"
+export { AtlasDropdownSelector } from "./atlasDropdown/atlasDropdown.component"
+export { AtlasLayerSelector } from "./atlasLayerSelector/atlasLayerSelector.component"
\ No newline at end of file
diff --git a/src/atlasComponents/uiSelectors/module.ts b/src/atlasComponents/uiSelectors/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2291f425c7c9e64f146e061790f4c17d07317f96
--- /dev/null
+++ b/src/atlasComponents/uiSelectors/module.ts
@@ -0,0 +1,29 @@
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module";
+import { UtilModule } from "src/util";
+import { DatabrowserModule } from "src/atlasComponents/databrowserModule";
+import { AtlasDropdownSelector } from "./atlasDropdown/atlasDropdown.component";
+import { AtlasLayerSelector, GetPreviewUrlPipe } from "./atlasLayerSelector/atlasLayerSelector.component";
+import {QuickTourModule} from "src/ui/quickTour/module";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    AngularMaterialModule,
+    UtilModule,
+    DatabrowserModule,
+    QuickTourModule
+  ],
+  declarations: [
+    AtlasDropdownSelector,
+    AtlasLayerSelector,
+    GetPreviewUrlPipe,
+  ],
+  exports: [
+    AtlasDropdownSelector,
+    AtlasLayerSelector,
+  ]
+})
+
+export class AtlasCmpUiSelectorsModule{}
diff --git a/src/atlasComponents/userAnnotations/annotationInterfaces.ts b/src/atlasComponents/userAnnotations/annotationInterfaces.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2d631452fa5d9eb02168ee5a078faf7148f651f5
--- /dev/null
+++ b/src/atlasComponents/userAnnotations/annotationInterfaces.ts
@@ -0,0 +1,48 @@
+
+export interface ViewerAnnotation {
+    id: string
+    position1: number[]
+    position2: number[]
+    name: string
+    description: string
+    type: string
+    circular: boolean
+    atlas: {name: string, id: string}
+    template: {name: string, id: string}
+    annotationVisible: boolean
+}
+
+export interface GroupedAnnotation {
+    id: string
+    position1?: number[]
+    position2?: number[]
+    annotations?: PolygonAnnotations[]
+    positions?: PolygonPositions[]
+    dimension?: string
+
+    name: string
+    description: string
+    type: string
+    circular?: boolean
+    atlas: {name: string, id: string}
+    template: {name: string, id: string}
+    annotationVisible: boolean
+}
+
+export interface PolygonAnnotations {
+    id: string
+    position1: number[]
+    position2: number[]
+}
+
+export interface PolygonPositions {
+    position: number[]
+    lines: {id: string, point: number}[]
+}
+
+export interface AnnotationType {
+    name: string
+    class: string
+    type: string
+    action: string
+}
diff --git a/src/atlasComponents/userAnnotations/annotationList/annotationList.component.ts b/src/atlasComponents/userAnnotations/annotationList/annotationList.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..525f5eaa5dad6a2ca706664963bda383d8bef9ac
--- /dev/null
+++ b/src/atlasComponents/userAnnotations/annotationList/annotationList.component.ts
@@ -0,0 +1,133 @@
+import {Component, ViewChild} from "@angular/core";
+import {ARIA_LABELS} from "common/constants";
+import { ModularUserAnnotationToolService } from "../tools/service";
+import { IAnnotationGeometry, TExportFormats } from "../tools/type";
+import { ComponentStore } from "src/viewerModule/componentStore";
+import { map, startWith, tap } from "rxjs/operators";
+import { Observable } from "rxjs";
+import { TZipFileConfig } from "src/zipFilesOutput/type";
+import { TFileInputEvent } from "src/getFileInput/type";
+import { FileInputDirective } from "src/getFileInput/getFileInput.directive";
+import { MatSnackBar } from "@angular/material/snack-bar";
+import { unzip } from "src/zipFilesOutput/zipFilesOutput.directive";
+
+const README = 'EXAMPLE OF READ ME TEXT'
+
+@Component({
+  selector: 'annotation-list',
+  templateUrl: './annotationList.template.html',
+  styleUrls: ['./annotationList.style.css'],
+  providers: [
+    ComponentStore,
+  ],
+  exportAs: 'annotationListCmp'
+})
+export class AnnotationList {
+
+  public ARIA_LABELS = ARIA_LABELS
+
+
+  @ViewChild(FileInputDirective)
+  fileInput: FileInputDirective
+
+  public managedAnnotations$ = this.annotSvc.spaceFilteredManagedAnnotations$
+
+  public manAnnExists$ = this.managedAnnotations$.pipe(
+    map(arr => !!arr && arr.length > 0),
+    startWith(false)
+  )
+
+  public filesExport$: Observable<TZipFileConfig[]> = this.managedAnnotations$.pipe(
+    startWith([] as IAnnotationGeometry[]),
+    map(manAnns => {
+      const readme = {
+        filename: 'README.md',
+        filecontent: README,
+      }
+      const annotationSands = manAnns.map(ann => {
+        return {
+          filename: `${ann.id}.sands.json`,
+          filecontent: JSON.stringify(ann.toSands(), null, 2),
+        }
+      })
+      return [ readme, ...annotationSands ]
+    })
+  )
+  constructor(
+    private annotSvc: ModularUserAnnotationToolService,
+    private snackbar: MatSnackBar,
+    cStore: ComponentStore<{ useFormat: TExportFormats }>,
+  ) {
+    cStore.setState({
+      useFormat: 'sands'
+    })
+  }
+
+  public hiddenAnnotations$ = this.annotSvc.hiddenAnnotations$
+  toggleManagedAnnotationVisibility(id: string) {
+    this.annotSvc.toggleAnnotationVisibilityById(id)
+  }
+
+  private parseAndAddAnnotation(input: string) {
+    const json = JSON.parse(input)
+    const annotation = this.annotSvc.parseAnnotationObject(json)
+    this.annotSvc.importAnnotation(annotation)
+  }
+
+  async handleImportEvent(ev: TFileInputEvent<'text' | 'file'>){
+    try {
+      const clearFileInputAndInform = () => {
+        if (this.fileInput) {
+          this.fileInput.clear()
+        }
+        this.snackbar.open('Annotation imported successfully!', 'Dismiss', {
+          duration: 3000
+        })
+      }
+
+      if (ev.type === 'text') {
+        const input = (ev as TFileInputEvent<'text'>).payload.input
+        /**
+         * parse as json, and go through the parsers
+         */
+        this.parseAndAddAnnotation(input)
+        clearFileInputAndInform()
+        return
+      }
+      if (ev.type === 'file') {
+        const files = (ev as TFileInputEvent<'file'>).payload.files
+        if (files.length === 0) throw new Error(`Need at least one file.`)
+        if (files.length > 1) throw new Error(`Parsing multiple files are not yet supported`)
+        const file = files[0]
+        const isJson = /\.json$/.test(file.name)
+        const isZip = /\.zip$/.test(file.name)
+        if (isZip) {
+          const files = await unzip(file)
+          const sands = files.filter(f => /\.json$/.test(f.filename))
+          for (const sand of sands) {
+            this.parseAndAddAnnotation(sand.filecontent)
+          }
+          clearFileInputAndInform()
+        }
+        if (isJson) {
+          const reader = new FileReader()
+          reader.onload = evt => {
+            const out = evt.target.result
+            this.parseAndAddAnnotation(out as string)
+            clearFileInputAndInform()
+          }
+          reader.onerror = e => { throw e }
+          reader.readAsText(file, 'utf-8')
+        }
+        /**
+         * check if zip or json
+         */
+        return
+      }
+    } catch (e) {
+      this.snackbar.open(`Error importing: ${e.toString()}`, 'Dismiss', {
+        duration: 3000
+      })
+    }
+  }
+}
diff --git a/src/atlasComponents/userAnnotations/annotationList/annotationList.style.css b/src/atlasComponents/userAnnotations/annotationList/annotationList.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..1f57a5f2d7a2155d5fb5c398d7fb079ee9337258
--- /dev/null
+++ b/src/atlasComponents/userAnnotations/annotationList/annotationList.style.css
@@ -0,0 +1,10 @@
+.inactive-filter {
+    color: #bababa;
+}
+
+:host-context([darktheme="true"]) .hovering-header {
+    background-color: #737373;
+}
+:host-context([darktheme="false"]) .hovering-header {
+    background-color: rgb(245, 245, 245);
+}
diff --git a/src/atlasComponents/userAnnotations/annotationList/annotationList.template.html b/src/atlasComponents/userAnnotations/annotationList/annotationList.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..17ac890eafc6f4049b039ad7c36147a2d481642d
--- /dev/null
+++ b/src/atlasComponents/userAnnotations/annotationList/annotationList.template.html
@@ -0,0 +1,92 @@
+<mat-card class="mat-elevantion-z4 h-100">
+    <div class="sidenav-cover-header-container bg-50-grey-20">
+
+        <!-- title -->
+        <mat-card-title>
+            My Annotations
+        </mat-card-title>
+
+        <!-- actions -->
+        <mat-card-subtitle>
+            <!-- import -->
+            <ng-template #importMessageTmpl>
+                Please select a annotation file to import.
+            </ng-template>
+
+            <button mat-icon-button
+                (file-input-directive)="handleImportEvent($event)"
+                [file-input-directive-title]="ARIA_LABELS.USER_ANNOTATION_IMPORT"
+                [file-input-directive-text]="true"
+                [file-input-directive-file]="true"
+                [file-input-directive-message]="importMessageTmpl"
+                [attr.aria-label]="ARIA_LABELS.USER_ANNOTATION_IMPORT"
+                [matTooltip]="ARIA_LABELS.USER_ANNOTATION_IMPORT">
+                <i class="fas fa-folder-open"></i>
+            </button>
+
+            <!-- export -->
+            <button mat-icon-button
+                [zip-files-output]="filesExport$ | async"
+                zip-files-output-zip-filename="exported_annotations.zip"
+                [attr.aria-label]="ARIA_LABELS.USER_ANNOTATION_EXPORT"
+                [matTooltip]="ARIA_LABELS.USER_ANNOTATION_EXPORT"
+                [disabled]="!(manAnnExists$ | async)">
+                <i class="fas fa-download"></i>
+            </button>
+        </mat-card-subtitle>
+    </div>
+
+    <!-- content -->
+    <mat-card-content class="mt-4 ml-15px-n mr-15px-n pb-4">
+        <!-- list of annotations -->
+        <ng-template [ngIf]="managedAnnotations$ | async" [ngIfElse]="placeholderTmpl" let-managedAnnotations>
+
+            <mat-accordion *ngIf="managedAnnotations.length > 0; else placeholderTmpl"
+                [attr.aria-label]="ARIA_LABELS.USER_ANNOTATION_LIST"
+                class="h-100 d-flex flex-column overflow-auto">
+
+                <!-- expansion panel -->
+                <mat-expansion-panel *ngFor="let managedAnnotation of managedAnnotations"
+                    hideToggle>
+
+                    <mat-expansion-panel-header [ngClass]="{'highlight': managedAnnotation.highlighted }">
+                        <mat-panel-title class="d-flex align-items-center">
+
+                            <!-- toggle visibility -->
+                            <button
+                                mat-icon-button
+                                iav-stop="click"
+                                (click)="toggleManagedAnnotationVisibility(managedAnnotation.id)">
+                                <i [ngClass]="(hiddenAnnotations$ | async | annotationVisiblePipe : managedAnnotation) ?  'fa-eye' : 'fa-eye-slash'" class="fas"></i>
+                            </button>
+                            <span class="flex-shrink-1 flex-grow-1" [ngClass]="{ 'text-muted': !managedAnnotation.name }">
+                                {{ managedAnnotation | singleAnnotationNamePipe : managedAnnotation.name }}
+                            </span>    
+                            <i class="flex-shrink-0 flex-grow-0" [ngClass]="managedAnnotation | singleannotationClsIconPipe"></i>
+                        </mat-panel-title>
+                    </mat-expansion-panel-header>
+
+                    <!-- single annotation edit body -->
+                    <ng-template matExpansionPanelContent>
+                        <div class="d-flex">
+
+                            <!-- spacer for inset single-annotation-unit -->
+                            <div class="w-3em flex-grow-0 flex-shrink-0"></div>
+
+                            <single-annotation-unit [single-annotation-unit-annotation]="managedAnnotation"
+                                class="flex-grow-1 flex-shrink-1">
+                            </single-annotation-unit>
+                        </div>
+                    </ng-template>
+                </mat-expansion-panel>
+            </mat-accordion>
+        </ng-template>
+
+        <!-- place holder when no annotations exist -->
+        <ng-template #placeholderTmpl>
+            <div class="p-4 text-muted">
+                <p>Start by adding an annotation, or import existing annotations.</p>
+            </div>
+        </ng-template>
+    </mat-card-content>
+</mat-card>
diff --git a/src/atlasComponents/userAnnotations/annotationMode/annotationMode.component.ts b/src/atlasComponents/userAnnotations/annotationMode/annotationMode.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0bacfe6b2d2d0f92d2b292429dcc4682bd05f1f3
--- /dev/null
+++ b/src/atlasComponents/userAnnotations/annotationMode/annotationMode.component.ts
@@ -0,0 +1,73 @@
+import { Component, Inject, OnDestroy, Optional } from "@angular/core";
+import { Store } from "@ngrx/store";
+import { ModularUserAnnotationToolService } from "../tools/service";
+import { viewerStateSetViewerMode } from "src/services/state/viewerState.store.helper";
+import { ARIA_LABELS } from 'common/constants'
+import { ClickInterceptor, CLICK_INTERCEPTOR_INJECTOR, CONTEXT_MENU_ITEM_INJECTOR, TContextMenu } from "src/util";
+import { TContextArg } from "src/viewerModule/viewer.interface";
+import { TContextMenuReg } from "src/contextMenuModule";
+import { MatSnackBar } from "@angular/material/snack-bar";
+
+@Component({
+  selector: 'annotating-tools-panel',
+  templateUrl: './annotationMode.template.html',
+  styleUrls: ['./annotationMode.style.css']
+})
+export class AnnotationMode implements OnDestroy{
+
+  public ARIA_LABELS = ARIA_LABELS
+
+  public moduleAnnotationTypes: {
+    instance: {
+      name: string
+      iconClass: string
+    }
+    onClick: Function
+  }[] = []
+
+  private onDestroyCb: Function[] = []
+
+  constructor(
+    private store$: Store<any>,
+    private modularToolSvc: ModularUserAnnotationToolService,
+    snackbar: MatSnackBar,
+    @Optional() @Inject(CLICK_INTERCEPTOR_INJECTOR) clickInterceptor: ClickInterceptor,
+    @Optional() @Inject(CONTEXT_MENU_ITEM_INJECTOR) ctxMenuInterceptor: TContextMenu<TContextMenuReg<TContextArg<'nehuba' | 'threeSurfer'>>>
+  ) {
+    this.moduleAnnotationTypes = this.modularToolSvc.moduleAnnotationTypes
+    const stopClickProp = () => false
+    if (clickInterceptor) {
+      const { register, deregister } = clickInterceptor
+      register(stopClickProp)
+      this.onDestroyCb.push(() => deregister(stopClickProp))
+    }
+
+    if (ctxMenuInterceptor) {
+      const { deregister, register } = ctxMenuInterceptor
+      register(stopClickProp)
+      this.onDestroyCb.push(() => deregister(stopClickProp))
+    }
+
+    this.modularToolSvc.loadStoredAnnotations()
+      .catch(e => {
+        snackbar.open(`Loading annotations from storage failed: ${e.toString()}`, 'Dismiss', {
+          duration: 3000
+        })
+      })
+  }
+
+  exitAnnotationMode(){
+    this.store$.dispatch(
+      viewerStateSetViewerMode({
+        payload: null
+      })
+    )
+  }
+  deselectTools(){
+    this.modularToolSvc.deselectTools()
+  }
+
+  ngOnDestroy(){
+    while (this.onDestroyCb.length > 0) this.onDestroyCb.pop()()
+  }
+}
diff --git a/src/atlasComponents/userAnnotations/annotationMode/annotationMode.style.css b/src/atlasComponents/userAnnotations/annotationMode/annotationMode.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..c525622e4f3c7081ec4f3a6a65de4fed4c367c45
--- /dev/null
+++ b/src/atlasComponents/userAnnotations/annotationMode/annotationMode.style.css
@@ -0,0 +1,13 @@
+.tab-toggle-container
+{
+  margin-left: -3rem;
+  padding-top: 0;
+  padding-bottom: 0;
+}
+
+.tab-toggle
+{
+  margin: 0.25rem -1rem 0.25rem 0rem;
+  padding-right: 0;
+  padding-left: 1.5rem;
+}
\ No newline at end of file
diff --git a/src/atlasComponents/userAnnotations/annotationMode/annotationMode.template.html b/src/atlasComponents/userAnnotations/annotationMode/annotationMode.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..a21e682b08bc6b9e7adcab3737e1261fe63d9e5a
--- /dev/null
+++ b/src/atlasComponents/userAnnotations/annotationMode/annotationMode.template.html
@@ -0,0 +1,25 @@
+<mat-card class="d-inline-flex flex-column pe-all tab-toggle-container"
+    [iav-key-listener]="[{ type: 'keydown', key: 'Escape', target: 'document', capture: true }]"
+    (iav-key-event)="deselectTools()">
+
+    <button
+        *ngFor="let moduleAnnotType of moduleAnnotationTypes"
+        mat-button
+        class="tab-toggle"
+        (click)="moduleAnnotType.onClick()"
+        [color]="(moduleAnnotType.instance.toolSelected$ | async) ? 'primary' : 'basic'"
+        type="button">
+        <i [class]="moduleAnnotType.instance.iconClass"></i>
+    </button>
+
+    <mat-divider class="d-block"></mat-divider>
+
+    <button
+        mat-button
+        (click)="exitAnnotationMode()"
+        class="tab-toggle"
+        [matTooltip]="ARIA_LABELS.EXIT_ANNOTATION_MODE"
+        color="warn">
+        <i class="fas fa-times"></i>
+    </button>
+</mat-card>
\ No newline at end of file
diff --git a/src/atlasComponents/userAnnotations/annotationVisible.pipe.ts b/src/atlasComponents/userAnnotations/annotationVisible.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..99f7c04de5deabc44875026082b1339773106523
--- /dev/null
+++ b/src/atlasComponents/userAnnotations/annotationVisible.pipe.ts
@@ -0,0 +1,13 @@
+import { Pipe, PipeTransform } from "@angular/core";
+import { IAnnotationGeometry } from "./tools/type";
+
+@Pipe({
+  name: 'annotationVisiblePipe',
+  pure: true
+})
+
+export class AnnotationVisiblePipe implements PipeTransform{
+  public transform(hiddenAnns: IAnnotationGeometry[], thisAnn: IAnnotationGeometry): boolean {
+    return hiddenAnns.findIndex(a => a.id === thisAnn.id) < 0
+  }
+}
diff --git a/src/atlasComponents/userAnnotations/directives/annotationEv.directive.ts b/src/atlasComponents/userAnnotations/directives/annotationEv.directive.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8e92f09776e2b5eaecb653061fda92af02bbf981
--- /dev/null
+++ b/src/atlasComponents/userAnnotations/directives/annotationEv.directive.ts
@@ -0,0 +1,42 @@
+import { Directive, EventEmitter, Input, OnDestroy, Output } from "@angular/core";
+import { Subscription } from "rxjs";
+import { ModularUserAnnotationToolService } from "../tools/service";
+import { TCallback } from "../tools/type";
+
+type TAnnotationEv<T extends keyof TCallback> = {
+  type: T
+  callArg?: TCallback[T]['callArg']
+}
+
+@Directive({
+  selector: '[annotation-event-directive]',
+  exportAs: 'annotationEventDir'
+})
+export class AnnotationEventDirective implements OnDestroy{
+
+  @Input('annotation-event-directive-filter')
+  filter: (keyof TCallback)[] = null
+
+  @Output('annotation-event-directive')
+  ev = new EventEmitter<TAnnotationEv<keyof TCallback>>()
+
+  private subs: Subscription[] = []
+
+  constructor(svc: ModularUserAnnotationToolService){
+    this.subs.push(
+      svc.toolEvents.subscribe(<T extends keyof TCallback>(event: { type: T } & TCallback[T]['callArg']) => {
+        if (this.filter?.includes) {
+          if (this.filter.includes(event.type)) {
+            this.ev.emit(event)
+          }
+        } else {
+          this.ev.emit(event)
+        }
+      })
+    )
+  }
+
+  ngOnDestroy(){
+    while(this.subs.length > 0)this.subs.pop().unsubscribe()
+  }
+}
diff --git a/src/atlasComponents/userAnnotations/directives/annotationSwitch.directive.ts b/src/atlasComponents/userAnnotations/directives/annotationSwitch.directive.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1109fbdbc342124d842fe82b59ee02c2a51d0de4
--- /dev/null
+++ b/src/atlasComponents/userAnnotations/directives/annotationSwitch.directive.ts
@@ -0,0 +1,53 @@
+import { Directive, HostListener, Inject, Input, Optional } from "@angular/core";
+import { viewerStateSetViewerMode } from "src/services/state/viewerState/actions";
+import { ARIA_LABELS } from "common/constants";
+import { select, Store } from "@ngrx/store";
+import { TContextArg } from "src/viewerModule/viewer.interface";
+import { TContextMenuReg } from "src/contextMenuModule";
+import { CONTEXT_MENU_ITEM_INJECTOR, TContextMenu } from "src/util";
+import { ModularUserAnnotationToolService } from "../tools/service";
+import { Subscription } from "rxjs";
+import { viewerStateViewerModeSelector } from "src/services/state/viewerState/selectors";
+
+@Directive({
+  selector: '[annotation-switch]'
+})
+export class AnnotationSwitch {
+
+  @Input('annotation-switch-mode')
+  mode: 'toggle' | 'off' | 'on' = 'on'
+
+  private currMode = null
+  private subs: Subscription[] = []
+  constructor(
+    private store$: Store<any>,
+    private svc: ModularUserAnnotationToolService,
+    @Optional() @Inject(CONTEXT_MENU_ITEM_INJECTOR) ctxMenuInterceptor: TContextMenu<TContextMenuReg<TContextArg<'nehuba' | 'threeSurfer'>>>
+  ) {
+    this.subs.push(
+      this.store$.pipe(
+        select(viewerStateViewerModeSelector)
+      ).subscribe(mode => {
+        this.currMode = mode
+      })
+    )
+  }
+
+  @HostListener('click')
+  onClick() {
+    let payload = null
+    if (this.mode === 'on') payload = ARIA_LABELS.VIEWER_MODE_ANNOTATING
+    if (this.mode === 'off') {
+      if (this.currMode === ARIA_LABELS.VIEWER_MODE_ANNOTATING) payload = null
+      else return
+    }
+    if (this.mode === 'toggle') {
+      payload = this.currMode === ARIA_LABELS.VIEWER_MODE_ANNOTATING
+        ? null
+        : ARIA_LABELS.VIEWER_MODE_ANNOTATING
+    }
+    this.store$.dispatch(
+      viewerStateSetViewerMode({ payload })
+    )
+  }
+}
diff --git a/src/atlasComponents/userAnnotations/filterAnnotationBySpace.pipe.ts b/src/atlasComponents/userAnnotations/filterAnnotationBySpace.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..69506ed32fd960dbe4da329884fa164d4727469c
--- /dev/null
+++ b/src/atlasComponents/userAnnotations/filterAnnotationBySpace.pipe.ts
@@ -0,0 +1,13 @@
+import { Pipe, PipeTransform } from "@angular/core";
+import { IAnnotationGeometry } from "./tools/type";
+
+@Pipe({
+  name: 'filterAnnotationsBySpace',
+  pure: true
+})
+
+export class FilterAnnotationsBySpace implements PipeTransform{
+  public transform(annotations: IAnnotationGeometry[], space: { '@id': string }): IAnnotationGeometry[]{
+    return annotations.filter(ann => ann.space["@id"] === space["@id"])
+  }
+}
\ No newline at end of file
diff --git a/src/atlasComponents/userAnnotations/index.ts b/src/atlasComponents/userAnnotations/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6da0cda7d75c0521617ec52270af1cb695ee7776
--- /dev/null
+++ b/src/atlasComponents/userAnnotations/index.ts
@@ -0,0 +1,3 @@
+export { AnnotationMode } from "./annotationMode/annotationMode.component";
+export { AnnotationList } from "./annotationList/annotationList.component";
+export { UserAnnotationsModule } from "./module";
diff --git a/src/atlasComponents/userAnnotations/module.ts b/src/atlasComponents/userAnnotations/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c4ecb05849b239e3528e7c411769ec474dd98555
--- /dev/null
+++ b/src/atlasComponents/userAnnotations/module.ts
@@ -0,0 +1,49 @@
+import { NgModule } from "@angular/core";
+import { CommonModule } from "@angular/common";
+import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module";
+import { FormsModule, ReactiveFormsModule } from "@angular/forms";
+import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
+import { AnnotationMode } from "src/atlasComponents/userAnnotations/annotationMode/annotationMode.component";
+import { AnnotationList } from "src/atlasComponents/userAnnotations/annotationList/annotationList.component";
+import { UserAnnotationToolModule } from "./tools/module";
+import { AnnotationSwitch } from "src/atlasComponents/userAnnotations/directives/annotationSwitch.directive";
+import { UtilModule } from "src/util";
+import { SingleAnnotationClsIconPipe, SingleAnnotationNamePipe, SingleAnnotationUnit } from "./singleAnnotationUnit/singleAnnotationUnit.component";
+import { AnnotationVisiblePipe } from "./annotationVisible.pipe";
+import { FileInputModule } from "src/getFileInput/module";
+import { ZipFilesOutputModule } from "src/zipFilesOutput/module";
+import { FilterAnnotationsBySpace } from "./filterAnnotationBySpace.pipe";
+import { AnnotationEventDirective } from "./directives/annotationEv.directive";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    BrowserAnimationsModule,
+    FormsModule,
+    ReactiveFormsModule,
+    AngularMaterialModule,
+    UserAnnotationToolModule,
+    UtilModule,
+    FileInputModule,
+    ZipFilesOutputModule,
+  ],
+  declarations: [
+    AnnotationMode,
+    AnnotationList,
+    AnnotationSwitch,
+    SingleAnnotationUnit,
+    SingleAnnotationNamePipe,
+    SingleAnnotationClsIconPipe,
+    AnnotationVisiblePipe,
+    FilterAnnotationsBySpace,
+    AnnotationEventDirective,
+  ],
+  exports: [
+    AnnotationMode,
+    AnnotationList,
+    AnnotationSwitch,
+    AnnotationEventDirective
+  ]
+})
+
+export class UserAnnotationsModule{}
diff --git a/src/atlasComponents/userAnnotations/singleAnnotationUnit/singleAnnotationUnit.component.ts b/src/atlasComponents/userAnnotations/singleAnnotationUnit/singleAnnotationUnit.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..299e8ccf558d42e793637ff2a3d95ff3f1e12b77
--- /dev/null
+++ b/src/atlasComponents/userAnnotations/singleAnnotationUnit/singleAnnotationUnit.component.ts
@@ -0,0 +1,136 @@
+import { AfterViewInit, Component, ComponentFactoryResolver, Inject, Injector, Input, OnDestroy, Optional, Pipe, PipeTransform, ViewChild, ViewContainerRef } from "@angular/core";
+import { IAnnotationGeometry, UDPATE_ANNOTATION_TOKEN } from "../tools/type";
+import { Point } from '../tools/point'
+import { Polygon } from '../tools/poly'
+import { FormControl, FormGroup } from "@angular/forms";
+import { Subscription } from "rxjs";
+import { select, Store } from "@ngrx/store";
+import { viewerStateFetchedAtlasesSelector } from "src/services/state/viewerState/selectors";
+import { ModularUserAnnotationToolService } from "../tools/service";
+import { MatSnackBar } from "@angular/material/snack-bar";
+import { Line } from "../tools/line";
+
+@Component({
+  selector: 'single-annotation-unit',
+  templateUrl: './singleAnnotationUnit.template.html',
+  styleUrls: [
+    './singleAnnotationUnit.style.css',
+  ]
+})
+
+export class SingleAnnotationUnit implements OnDestroy, AfterViewInit{
+  @Input('single-annotation-unit-annotation')
+  public managedAnnotation: IAnnotationGeometry
+
+  public formGrp: FormGroup
+
+  @ViewChild('editAnnotationVCRef', { read: ViewContainerRef })
+  editAnnotationVCR: ViewContainerRef
+
+  private chSubs: Subscription[] = []
+  private subs: Subscription[] = []
+  public templateSpaces: {
+    ['@id']: string
+    name: string
+  }[] = []
+  ngOnChanges(){
+    while(this.chSubs.length > 0) this.chSubs.pop().unsubscribe()
+    
+    this.formGrp = new FormGroup({
+      name: new FormControl(this.managedAnnotation.name),
+      spaceId: new FormControl({
+        value: this.managedAnnotation.space["@id"],
+        disabled: true
+      }),
+      desc: new FormControl(this.managedAnnotation.desc),
+    })
+
+    this.chSubs.push(
+      this.formGrp.valueChanges.subscribe(value => {
+        const { name, desc, spaceId } = value
+        this.managedAnnotation.setName(name)
+        this.managedAnnotation.setDesc(desc)
+      })
+    )
+
+  }
+
+  constructor(
+    store: Store<any>,
+    private snackbar: MatSnackBar,
+    private svc: ModularUserAnnotationToolService,
+    private cfr: ComponentFactoryResolver,
+    private injector: Injector,
+  ){
+    this.subs.push(
+      store.pipe(
+        select(viewerStateFetchedAtlasesSelector),
+      ).subscribe(atlases => {
+        for (const atlas of atlases) {
+          for (const tmpl of atlas.templateSpaces) {
+            this.templateSpaces.push({
+              '@id': tmpl['@id'],
+              name: tmpl.name
+            })
+          }
+        }
+      })
+    )
+  }
+
+  ngAfterViewInit(){
+    if (this.managedAnnotation && this.editAnnotationVCR) {
+      const editCmp = this.svc.getEditAnnotationCmp(this.managedAnnotation)
+      if (!editCmp) {
+        this.snackbar.open(`Update component not found!`, 'Dismiss', {
+          duration: 3000
+        })
+        throw new Error(`Edit component not found!`)
+      }
+      const cf = this.cfr.resolveComponentFactory(editCmp)
+      
+      const injector = Injector.create({
+        providers: [{
+          provide: UDPATE_ANNOTATION_TOKEN,
+          useValue: this.managedAnnotation
+        }],
+        parent: this.injector
+      })
+      this.editAnnotationVCR.createComponent(cf, null, injector)
+    }
+  }
+
+  ngOnDestroy(){
+    while (this.subs.length > 0) this.subs.pop().unsubscribe()
+  }
+
+}
+
+@Pipe({
+  name: 'singleAnnotationNamePipe',
+  pure: true
+})
+
+export class SingleAnnotationNamePipe implements PipeTransform{
+  public transform(ann: IAnnotationGeometry, name?: string): string{
+    if (name) return name
+    if (ann instanceof Polygon) return `Unnamed Polygon`
+    if (ann instanceof Point) return `Unname Point`
+    if (ann instanceof Line) return `Unnamed Line`
+    return `Unnamed geometry`
+  }
+}
+
+@Pipe({
+  name: 'singleannotationClsIconPipe',
+  pure: true
+})
+
+export class SingleAnnotationClsIconPipe implements PipeTransform{
+  public transform(ann: IAnnotationGeometry): string{
+    if (ann instanceof Polygon) return `fas fa-draw-polygon`
+    if (ann instanceof Point) return `fas fa-circle`
+    if (ann instanceof Line) return `fas fa-slash`
+    return `fas fa-mouse-pointer`
+  }
+}
\ No newline at end of file
diff --git a/src/ui/nehubaContainer/maximisePanelButton/maximisePanelButton.style.css b/src/atlasComponents/userAnnotations/singleAnnotationUnit/singleAnnotationUnit.style.css
similarity index 100%
rename from src/ui/nehubaContainer/maximisePanelButton/maximisePanelButton.style.css
rename to src/atlasComponents/userAnnotations/singleAnnotationUnit/singleAnnotationUnit.style.css
diff --git a/src/atlasComponents/userAnnotations/singleAnnotationUnit/singleAnnotationUnit.template.html b/src/atlasComponents/userAnnotations/singleAnnotationUnit/singleAnnotationUnit.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..e03b3068ebc2360cc9e6a08c537c73c750c4ece9
--- /dev/null
+++ b/src/atlasComponents/userAnnotations/singleAnnotationUnit/singleAnnotationUnit.template.html
@@ -0,0 +1,38 @@
+<form [formGroup]="formGrp" class="d-flex flex-column" autocomplete="off">
+  <mat-form-field>
+    <mat-label>
+      Space
+    </mat-label>
+    <mat-select formControlName="spaceId">
+      <mat-option *ngFor="let tmpl of templateSpaces" [value]="tmpl['@id']">
+        {{ tmpl.name }}
+      </mat-option>
+    </mat-select>
+  </mat-form-field>
+
+  <mat-form-field>
+    <mat-label>
+      Name
+    </mat-label>
+    <input
+      type="text"
+      matInput
+      formControlName="name"
+      [placeholder]="managedAnnotation | singleAnnotationNamePipe">
+  </mat-form-field>
+
+  <mat-form-field>
+    <mat-label>
+      Description
+    </mat-label>
+    <textarea
+      matInput
+      formControlName="desc">
+    </textarea>
+  </mat-form-field>
+</form>
+
+<mat-divider class="m-2 d-block position-relative"></mat-divider>
+
+<ng-template #editAnnotationVCRef>
+</ng-template>
diff --git a/src/atlasComponents/userAnnotations/tools/delete.ts b/src/atlasComponents/userAnnotations/tools/delete.ts
new file mode 100644
index 0000000000000000000000000000000000000000..dc817a65012bd3dbe5409fb8656d5b9d5bcb9990
--- /dev/null
+++ b/src/atlasComponents/userAnnotations/tools/delete.ts
@@ -0,0 +1,76 @@
+import { OnDestroy } from "@angular/core";
+import { Observable, Subject, Subscription } from "rxjs";
+import { filter, switchMapTo, takeUntil, withLatestFrom } from "rxjs/operators";
+import { Point } from "./point";
+import { AbsToolClass, IAnnotationEvents, IAnnotationGeometry, IAnnotationTools, TAnnotationEvent, TCallbackFunction, TNgAnnotationPoint, TToolType } from "./type";
+
+export class ToolDelete extends AbsToolClass<Point> implements IAnnotationTools, OnDestroy {
+
+  public subs: Subscription[] = []
+  toolType: TToolType = 'deletion'
+  iconClass = 'fas fa-trash'
+  name = 'Delete'
+
+  onMouseMoveRenderPreview(){
+    return []
+  }
+
+  // eslint-disable-next-line @typescript-eslint/no-empty-function
+  addAnnotation(){}
+  
+  // eslint-disable-next-line @typescript-eslint/no-empty-function
+  removeAnnotation(){}
+
+  managedAnnotations$ = new Subject<Point[]>()
+  private allManAnnotations: IAnnotationGeometry[] = []
+  
+  constructor(
+    annotationEv$: Observable<TAnnotationEvent<keyof IAnnotationEvents>>,
+    callback: TCallbackFunction
+  ){
+    super(annotationEv$, callback)
+    this.init()
+  }
+  init(){
+    if (this.callback) {
+      const obs$ = this.callback({ type: 'requestManAnnStream' })
+      if (!obs$) throw new Error(`Error requestManAnnStream`)
+
+      const toolDeselect$ = this.toolSelected$.pipe(
+        filter(flag => !flag)
+      )
+      const toolSelThenClick$ = this.toolSelected$.pipe(
+        filter(flag => !!flag),
+        switchMapTo(this.mouseClick$.pipe(
+          takeUntil(toolDeselect$)
+        ))
+      )
+
+      this.subs.push(
+        /**
+         * Get stream of all managed annotations
+         */
+        obs$.subscribe(manAnn => {
+          this.allManAnnotations = manAnn
+        }),
+        toolSelThenClick$.pipe(
+          withLatestFrom(this.hoverAnnotation$)
+        ).subscribe(([ _, ev ]) => {
+          const annId = ev?.detail?.pickedAnnotationId
+          if (!annId) return
+          for (const manan of this.allManAnnotations) {
+            if (manan.getNgAnnotationIds().indexOf(annId) >= 0) {
+              manan.remove()
+              return
+            }
+          }
+        })
+      )
+    }
+  }
+
+
+  ngOnDestroy(){
+    while (this.subs.length > 0) this.subs.pop().unsubscribe()
+  }
+}
diff --git a/src/atlasComponents/userAnnotations/tools/line.ts b/src/atlasComponents/userAnnotations/tools/line.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1303612f991d1cadc53a1ce73ddf9f043babca52
--- /dev/null
+++ b/src/atlasComponents/userAnnotations/tools/line.ts
@@ -0,0 +1,331 @@
+import {
+  IAnnotationTools,
+  IAnnotationGeometry,
+  TAnnotationEvent,
+  IAnnotationEvents,
+  AbsToolClass,
+  INgAnnotationTypes,
+  TToolType,
+  TSandsLine,
+  getCoord,
+  TBaseAnnotationGeomtrySpec,
+  TCallbackFunction,
+} from "./type";
+import { Point, TPointJsonSpec } from './point'
+import { OnDestroy } from "@angular/core";
+import { Observable, Subject, Subscription } from "rxjs";
+import { filter, switchMapTo, takeUntil } from "rxjs/operators";
+import { getUuid } from "src/util/fn";
+
+export type TLineJsonSpec = {
+  '@type': 'siibra-ex/annotation/line'
+  points: (TPointJsonSpec|Point)[]
+} & TBaseAnnotationGeomtrySpec
+
+export class Line extends IAnnotationGeometry{
+  public id: string
+
+  public points: Point[] = []
+
+  public hasPoint(p: Point) {
+    return this.points.indexOf(p) >= 0
+  }
+
+  public addLinePoints(p: Point | {x: number, y: number, z: number}): Point {
+    if (this.checkComplete()) {
+      throw new Error(`This line is already complete!`)
+    }
+    const point = p instanceof Point
+      ? p
+      : new Point({
+        id: `${this.id}_${getUuid()}`,
+        "@type": 'siibra-ex/annotation/point',
+        space: this.space,
+        ...p
+      })
+    if (!this.points[0]) this.points[0] = point
+    else this.points[1] = point
+    this.sendUpdateSignal()
+    return point
+  }
+
+  getNgAnnotationIds() {
+    return [this.id]
+  }
+
+  private checkComplete(){
+    return this.points.length >= 2
+  }
+
+  toString(): string {
+    if (!this.checkComplete()) {
+      return `Line incomplete.`
+    } 
+    
+    return `Line from ${this.points[0].toString()} to ${this.points[1].toString()}`
+  }
+
+  toSands(): TSandsLine {
+    if (!this.checkComplete()) {
+      return null
+    }
+    const {
+      x: x0, y: y0, z: z0
+    } = this.points[0]
+
+    const {
+      x: x1, y: y1, z: z1
+    } = this.points[1]
+
+    return {
+      '@id': this.id,
+      '@type': "tmp/line",
+      coordinateSpace: {
+        '@id': this.space["@id"]
+      },
+      coordinatesFrom: [getCoord(x0/1e6), getCoord(y0/1e6), getCoord(z0/1e6)],
+      coordinatesTo: [getCoord(x1/1e6), getCoord(y1/1e6), getCoord(z1/1e6)],
+    }
+  }
+
+  toNgAnnotation(): INgAnnotationTypes['line'][] {
+    if (!this.checkComplete()) return []
+    const pt1 = this.points[0]
+    const pt2 = this.points[1]
+    return [{
+      id: this.id,
+      pointA: [pt1.x, pt1.y, pt1.z],
+      pointB: [pt2.x, pt2.y, pt2.z],
+      type: 'line',
+      description: ''
+    }]
+
+  }
+
+  toJSON(): TLineJsonSpec{
+    const { id, name, desc, points, space } = this
+    return {
+      id,
+      name,
+      desc,
+      points: points.map(p => p.toJSON()),
+      space,
+      '@type': 'siibra-ex/annotation/line'
+    }
+  }
+
+  static fromJSON(json: TLineJsonSpec){
+    return new Line(json)
+  }
+
+  static fromSANDS(json: TSandsLine): Line{
+    const {
+      "@id": id,
+      "@type": type,
+      coordinateSpace,
+      coordinatesFrom,
+      coordinatesTo
+    } = json
+    if (type !== 'tmp/line') throw new Error(`cannot parse line from sands`)
+    const fromPt = coordinatesFrom.map(c => {
+      if (c.unit["@id"] !== 'id.link/mm') throw new Error(`Cannot parse unit`)
+      return c.value * 1e6
+    })
+    const toPoint = coordinatesTo.map(c => {
+      if (c.unit["@id"] !== 'id.link/mm') throw new Error(`Cannot parse unit`)
+      return c.value * 1e6
+    })
+    const line = new Line({
+      id,
+      "@type": "siibra-ex/annotation/line",
+      points: [
+        new Point({
+          "@type": 'siibra-ex/annotation/point',
+          x: fromPt[0],
+          y: fromPt[1],
+          z: fromPt[2],
+          space: coordinateSpace
+        }),
+        new Point({
+          '@type': "siibra-ex/annotation/point",
+          x: toPoint[0],
+          y: toPoint[1],
+          z: toPoint[2],
+          space: coordinateSpace
+        })
+      ],
+      space: coordinateSpace
+    })
+    return line
+  }
+
+  constructor(spec?: TLineJsonSpec){
+    super(spec)
+    const { points = [] } = spec || {}
+    this.points = points.map(p => {
+      if (p instanceof Point) return p
+      return Point.fromJSON(p)
+    })
+  }
+
+  public translate(x: number, y: number, z: number) {
+    for (const p of this.points){
+      p.translate(x, y, z)
+    }
+    this.sendUpdateSignal()
+  }
+
+  private sendUpdateSignal(){
+    this.updateSignal$.next(this.toString())
+  }
+}
+
+export const LINE_ICON_CLASS = 'fas fa-slash'
+
+export class ToolLine extends AbsToolClass<Line> implements IAnnotationTools, OnDestroy {
+  static PREVIEW_ID='tool_line_preview'
+  public name = 'Line'
+  public toolType: TToolType = 'drawing'
+  public iconClass = LINE_ICON_CLASS
+
+  private selectedLine: Line
+  
+  subs: Subscription[] = []
+
+  private managedAnnotations: Line[] = []
+  public managedAnnotations$ = new Subject<Line[]>()
+
+  onMouseMoveRenderPreview(pos: [number, number, number]) {
+    if (this.selectedLine && !!this.selectedLine.points[0]) {
+      const { x, y, z } = this.selectedLine.points[0]
+      return [{
+        id: `${ToolLine.PREVIEW_ID}_0`,
+        pointA: [ x, y, z ],
+        pointB: pos,
+        type: 'line',
+        description: ''
+      }] as INgAnnotationTypes['line'][]
+    }
+    return [{
+      id: `${ToolLine.PREVIEW_ID}_0`,
+      point: pos,
+      type: 'point',
+      description: ''
+    }] as INgAnnotationTypes['point'][]
+  }
+
+
+  constructor(
+    annotationEv$: Observable<TAnnotationEvent<keyof IAnnotationEvents>>,
+    callback?: TCallbackFunction
+  ){
+    super(annotationEv$, callback)
+
+    this.init()
+    
+    const toolDeselect$ = this.toolSelected$.pipe(
+      filter(flag => !flag)
+    )
+    const toolSelThenClick$ = this.toolSelected$.pipe(
+      filter(flag => !!flag),
+      switchMapTo(this.mouseClick$.pipe(
+        takeUntil(toolDeselect$)
+      ))
+    )
+
+    this.subs.push(
+      /**
+       * on end tool select
+       */
+      toolDeselect$.subscribe(() => {
+        /**
+         * if line is not completed by tool deselect
+         * it only has a single point, and should be removed
+         */
+        if (this.selectedLine) {
+          this.removeAnnotation(this.selectedLine.id)
+        }
+        this.selectedLine = null
+      }),
+      /**
+       * on tool selected
+       * on mouse down
+       * until tool deselected
+       */
+      toolSelThenClick$.pipe(
+      ).subscribe(mouseev => {
+        const crd = mouseev.detail.ngMouseEvent
+        if (!this.selectedLine) {
+          const newLine = new Line({
+            space: this.space,
+            "@type": 'siibra-ex/annotation/line',
+            points: []
+          })
+          newLine.addLinePoints(crd)
+          this.addAnnotation(newLine)
+          this.selectedLine = newLine
+        } else {
+
+          this.selectedLine.addLinePoints(crd)
+          this.selectedLine = null
+          this.managedAnnotations$.next(this.managedAnnotations)
+          if (this.callback) {
+            this.callback({
+              type: 'message',
+              message: 'Line added.',
+              action: 'Open',
+              actionCallback: () => this.callback({ type: 'showList' })
+            })
+            this.callback({ type: 'paintingEnd' })
+          }
+        }
+      }),
+
+      /**
+       * emit on init, and reset on mouseup$
+       * otherwise, pairwise confuses last drag event and first drag event
+       */
+      this.dragHoveredAnnotationsDelta$.subscribe(val => {
+        
+        const { ann, deltaX, deltaY, deltaZ } = val
+        const { pickedAnnotationId, pickedOffset } = ann.detail
+
+        const annotation = this.managedAnnotations.find(an => an.id === pickedAnnotationId)
+        if (!annotation) {
+          return null
+        }
+        
+        if (pickedOffset === 2) {
+          annotation.points[1].translate(deltaX, deltaY, deltaZ)
+        } else if (pickedOffset === 1) {
+          annotation.points[0].translate(deltaX, deltaY, deltaZ)
+        } else {
+          annotation.translate(deltaX, deltaY, deltaZ)
+        }
+        this.managedAnnotations$.next(this.managedAnnotations)
+      })
+    )
+  }
+
+  ngOnDestroy(){
+    this.subs.forEach(s => s.unsubscribe())
+  }
+
+  addAnnotation(line: Line) {
+    const idx = this.managedAnnotations.findIndex(ann => ann.id === line.id)
+    if (idx >= 0) throw new Error(`Line annotation has already been added`)
+    line.remove = () => this.removeAnnotation(line.id)
+    this.managedAnnotations.push(line)
+    this.managedAnnotations$.next(this.managedAnnotations)
+  }
+
+  removeAnnotation(id: string){
+    const idx = this.managedAnnotations.findIndex(ann => ann.id === id)
+    if (idx < 0) {
+      return
+    }
+    this.managedAnnotations.splice(idx, 1)
+    this.managedAnnotations$.next(this.managedAnnotations)
+  }
+
+}
diff --git a/src/atlasComponents/userAnnotations/tools/line/line.component.ts b/src/atlasComponents/userAnnotations/tools/line/line.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..bf3d32401fc9cfc2378b2a4d17000e53cef2390d
--- /dev/null
+++ b/src/atlasComponents/userAnnotations/tools/line/line.component.ts
@@ -0,0 +1,95 @@
+import { Component, ElementRef, Inject, Input, OnDestroy, Optional, ViewChild } from "@angular/core";
+import { MatSnackBar } from "@angular/material/snack-bar";
+import { Store } from "@ngrx/store";
+import { Line, LINE_ICON_CLASS } from "../line";
+import { ToolCmpBase } from "../toolCmp.base";
+import { IAnnotationGeometry, TExportFormats, UDPATE_ANNOTATION_TOKEN } from "../type";
+import { viewerStateChangeNavigation } from "src/services/state/viewerState/actions";
+import { Point } from "../point";
+import { ARIA_LABELS } from 'common/constants'
+import { ComponentStore } from "src/viewerModule/componentStore";
+
+@Component({
+  selector: 'line-update-cmp',
+  templateUrl: './line.template.html',
+  styleUrls: [
+    './line.style.css'
+  ]
+})
+
+export class LineUpdateCmp extends ToolCmpBase implements OnDestroy{
+  @Input('update-annotation')
+  public updateAnnotation: Line
+
+  public ARIA_LABELS = ARIA_LABELS
+  public LINE_ICON_CLASS = LINE_ICON_CLASS
+
+  @ViewChild('copyTarget', { read: ElementRef, static: false })
+  copyTarget: ElementRef
+
+  constructor(
+    private store: Store<any>,
+    private snackbar: MatSnackBar,
+    cStore: ComponentStore<{ useFormat: TExportFormats }>,
+    @Optional() @Inject(UDPATE_ANNOTATION_TOKEN) updateAnnotation: IAnnotationGeometry,
+  ){
+    super(cStore)
+
+    if (updateAnnotation) {
+      if (updateAnnotation instanceof Line) {
+        this.updateAnnotation = updateAnnotation
+      }
+    }
+  }
+
+  ngOnDestroy(){
+    while (this.sub.length > 0) this.sub.pop().unsubscribe()
+  }
+
+  get copyValue(){
+    return this.copyTarget && this.copyTarget.nativeElement.value
+  }
+
+  gotoRoi(roi?: IAnnotationGeometry){
+    if (!this.updateAnnotation && !roi) {
+      throw new Error(`updateAnnotation undefined`)
+    }
+
+    if (roi && roi instanceof Point) {
+      const { x, y, z } = roi
+
+      this.store.dispatch(
+        viewerStateChangeNavigation({
+          navigation: {
+            position: [x, y, z],
+            positionReal: true,
+            animation: {}
+          }
+        })
+      )
+      return
+    }
+
+    if (this.updateAnnotation.points.length < 1) {
+      this.snackbar.open('No points added to polygon yet.', 'Dismiss', {
+        duration: 3000
+      })
+      return
+    }
+    const { x, y, z } = this.updateAnnotation.points[0]
+    
+    this.store.dispatch(
+      viewerStateChangeNavigation({
+        navigation: {
+          position: [x, y, z],
+          positionReal: true,
+          animation: {}
+        }
+      })
+    )
+  }
+
+  remove(){
+    this.updateAnnotation?.remove()
+  }
+}
diff --git a/src/atlasComponents/userAnnotations/tools/line/line.style.css b/src/atlasComponents/userAnnotations/tools/line/line.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..3cf6a85959217c0d3217a794e9dd0a1a7cbc460a
--- /dev/null
+++ b/src/atlasComponents/userAnnotations/tools/line/line.style.css
@@ -0,0 +1,5 @@
+:host
+{
+  display: flex;
+  flex-direction: column;
+}
\ No newline at end of file
diff --git a/src/atlasComponents/userAnnotations/tools/line/line.template.html b/src/atlasComponents/userAnnotations/tools/line/line.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..e0b3b06a78c4ebb58e26e4d9254f6c59e74a2ca3
--- /dev/null
+++ b/src/atlasComponents/userAnnotations/tools/line/line.template.html
@@ -0,0 +1,51 @@
+<!-- summary of geometry -->
+
+<span class="m-2 text-muted">
+  Endpoints
+</span>
+
+<mat-chip-list>
+  <mat-chip *ngFor="let point of (updateAnnotation?.points || []) ; let i = index"
+    (click)="gotoRoi(point)"
+    [matTooltip]="point">
+    {{ i }}
+  </mat-chip>
+</mat-chip-list>
+
+<mat-divider class="m-2 d-block position-relative"></mat-divider>
+
+<!-- actions -->
+
+<div class="d-flex">
+
+  <!-- export -->
+  <button mat-icon-button
+    [attr.aria-label]="ARIA_LABELS.USER_ANNOTATION_EXPORT_SINGLE"
+    [matTooltip]="ARIA_LABELS.USER_ANNOTATION_EXPORT_SINGLE"
+    [matMenuTriggerFor]="exportMenu">
+    <i class="fas fa-file-export"></i>
+  </button>
+
+  <!-- delete -->
+  <button mat-icon-button
+    [attr.aria-label]="ARIA_LABELS.USER_ANNOTATION_DELETE"
+    [matTooltip]="ARIA_LABELS.USER_ANNOTATION_DELETE"
+    (click)="remove()">
+    <i class="fas fa-trash"></i>
+  </button>
+</div>
+
+<mat-menu #exportMenu="matMenu" xPosition="before">
+  <div class="iv-custom-comp card text"
+    iav-stop="click">
+
+    <div class="iv-custom-comp text">
+      <textarea-copy-export
+        [textarea-copy-export-label]="useFormat"
+        [textarea-copy-export-text]="updateAnnotation.updateSignal$ | async | toFormattedStringPipe : updateAnnotation : useFormat"
+        [textarea-copy-export-download-filename]="updateAnnotation.id + '.sands.json'"
+        [textarea-copy-export-disable]="true">
+      </textarea-copy-export>
+    </div>
+  </div>
+</mat-menu>
diff --git a/src/atlasComponents/userAnnotations/tools/module.ts b/src/atlasComponents/userAnnotations/tools/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..869c307409d53b76d9b409954d32a0acca2f5341
--- /dev/null
+++ b/src/atlasComponents/userAnnotations/tools/module.ts
@@ -0,0 +1,80 @@
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { Subject } from "rxjs";
+import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module";
+import { UtilModule } from "src/util";
+import { LineUpdateCmp } from "./line/line.component";
+import { PointUpdateCmp } from "./point/point.component";
+import { PolyUpdateCmp } from "./poly/poly.component";
+import { ModularUserAnnotationToolService } from "./service";
+import { ToFormattedStringPipe } from "./toFormattedString.pipe";
+import { ANNOTATION_EVENT_INJ_TOKEN } from "./type";
+import { Line, ToolLine } from "src/atlasComponents/userAnnotations/tools/line";
+import { Point, ToolPoint } from "./point";
+import { ToolSelect } from "./select";
+import { ToolDelete } from "./delete";
+import { Polygon, ToolPolygon } from "./poly";
+import { ZipFilesOutputModule } from "src/zipFilesOutput/module";
+import { TextareaCopyExportCmp } from "./textareaCopyExport/textareaCopyExport.component";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    AngularMaterialModule,
+    UtilModule,
+    ZipFilesOutputModule,
+  ],
+  declarations: [
+    LineUpdateCmp,
+    PolyUpdateCmp,
+    PointUpdateCmp,
+    ToFormattedStringPipe,
+    TextareaCopyExportCmp,
+  ],
+  exports: [
+    LineUpdateCmp,
+    PolyUpdateCmp,
+    PointUpdateCmp,
+  ],
+  providers: [
+    {
+      provide: ANNOTATION_EVENT_INJ_TOKEN,
+      useValue: new Subject()
+    },
+    ModularUserAnnotationToolService
+  ]
+})
+
+export class UserAnnotationToolModule {
+
+  constructor(
+    svc: ModularUserAnnotationToolService,
+  ){
+    const selTool = svc.registerTool({
+      toolCls: ToolSelect
+    })
+    svc.defaultTool = selTool
+
+    svc.registerTool({
+      toolCls: ToolPoint,
+      target: Point,
+      editCmp: PointUpdateCmp,
+    })
+
+    svc.registerTool({
+      toolCls: ToolLine,
+      target: Line,
+      editCmp: LineUpdateCmp,
+    })
+
+    svc.registerTool({
+      toolCls: ToolPolygon,
+      target: Polygon,
+      editCmp: PolyUpdateCmp,
+    })
+
+    svc.registerTool({
+      toolCls: ToolDelete
+    })
+  }
+}
diff --git a/src/atlasComponents/userAnnotations/tools/point.ts b/src/atlasComponents/userAnnotations/tools/point.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2dcafc2490a82b2dacb3bf5180b7457947de2770
--- /dev/null
+++ b/src/atlasComponents/userAnnotations/tools/point.ts
@@ -0,0 +1,213 @@
+import { AbsToolClass, getCoord, IAnnotationEvents, IAnnotationGeometry, IAnnotationTools, INgAnnotationTypes, TAnnotationEvent, TBaseAnnotationGeomtrySpec, TCallbackFunction, TNgAnnotationEv, TSandsPoint, TToolType } from "./type";
+import { merge, Observable, Subject, Subscription } from "rxjs";
+import { OnDestroy } from "@angular/core";
+import { filter, switchMapTo, takeUntil } from "rxjs/operators";
+
+export type TPointJsonSpec = {
+  x: number
+  y: number
+  z: number
+  '@type': 'siibra-ex/annotation/point'
+} & TBaseAnnotationGeomtrySpec
+
+export class Point extends IAnnotationGeometry {
+  id: string
+  x: number
+  y: number
+  z: number
+
+  static threshold = 1e-6
+  static eql(p1: Point, p2: Point) {
+    return Math.abs(p1.x - p2.x) < Point.threshold
+      && Math.abs(p1.y - p2.y) < Point.threshold
+      && Math.abs(p1.z - p2.z) < Point.threshold
+  }
+  constructor(spec?: TPointJsonSpec){
+    super(spec)
+
+    this.x = spec.x || 0
+    this.y = spec.y || 0
+    this.z = spec.z || 0
+  }
+  toJSON(): TPointJsonSpec{
+    const { id, x, y, z, space, name, desc } = this
+    return { id, x, y, z, space, name, desc, '@type': 'siibra-ex/annotation/point' }
+  }
+
+  getNgAnnotationIds(){
+    return [this.id]
+  }
+  toNgAnnotation(): INgAnnotationTypes['point'][]{
+    return [{
+      id: this.id,
+      point: [this.x, this.y, this.z],
+      type: 'point',
+    }]
+  }
+  static fromJSON(json: TPointJsonSpec) {
+    return new Point(json)
+  }
+
+  static fromSANDS(sands: TSandsPoint): Point {
+    const {
+      "@id": id,
+      "@type": type,
+      coordinateSpace,
+      coordinates
+    } = sands
+    if (type === 'https://openminds.ebrains.eu/sands/CoordinatePoint') {
+      const parsedCoordinate = coordinates.map(coord => {
+        const { value, unit } = coord
+        if (unit["@id"] !== 'id.link/mm') throw new Error(`Unit does not parse`)
+        return value * 1e6
+      })
+      const point = new Point({
+        id,
+        space: coordinateSpace,
+        "@type": 'siibra-ex/annotation/point',
+        x: parsedCoordinate[0],
+        y: parsedCoordinate[1],
+        z: parsedCoordinate[2],
+      })
+      return point
+    }
+
+    throw new Error(`cannot parse sands for points, @type mismatch`)
+  }
+
+  toString(){
+    return `${(this.x / 1e6).toFixed(2)}mm, ${(this.y / 1e6).toFixed(2)}mm, ${(this.z / 1e6).toFixed(2)}mm`
+  }
+
+  toSands(): TSandsPoint{
+    const {x, y, z} = this
+    return {
+      '@id': this.id,
+      '@type': 'https://openminds.ebrains.eu/sands/CoordinatePoint',
+      coordinateSpace: {
+        '@id': this.space["@id"]
+      },
+      coordinates:[ getCoord(x/1e6), getCoord(y/1e6), getCoord(z/1e6) ]
+    }
+  }
+
+  public translate(x: number, y: number, z: number): void {
+    this.x += x
+    this.y += y
+    this.z += z
+    this.updateSignal$.next(this.toString())
+  }
+}
+
+export const POINT_ICON_CLASS='fas fa-circle'
+
+export class ToolPoint extends AbsToolClass<Point> implements IAnnotationTools, OnDestroy {
+  static PREVIEW_ID='tool_point_preview'
+  public name = 'Point'
+  public toolType: TToolType = 'drawing'
+  public iconClass = POINT_ICON_CLASS
+  
+  public subs: Subscription[] = []
+  private managedAnnotations: Point[] = []
+  public managedAnnotations$ = new Subject<Point[]>()
+
+  constructor(
+    annotationEv$: Observable<TAnnotationEvent<keyof IAnnotationEvents>>,
+    callback: TCallbackFunction
+  ){
+    super(annotationEv$, callback)
+    this.init()
+    const toolDeselect$ = this.toolSelected$.pipe(
+      filter(flag => !flag)
+    )
+
+    const toolSelThenClick$ = this.toolSelected$.pipe(
+      filter(flag => !!flag),
+      switchMapTo(this.mouseClick$.pipe(
+        takeUntil(toolDeselect$)
+      ))
+    )
+
+    this.subs.push(
+      /**
+       * listen to click ev, add point when it occurrs
+       */
+      toolSelThenClick$.subscribe(ev => {
+        const {x, y, z} = ev.detail.ngMouseEvent
+        const { space } = this
+        const pt = new Point({
+          x, y, z,
+          space,
+          '@type': 'siibra-ex/annotation/point'
+        })
+        this.addAnnotation(pt)
+
+        if (this.callback) {
+
+          /**
+           * message
+           */
+          this.callback({
+            type: 'message',
+            message: `Point added`,
+            action: 'Open',
+            actionCallback: () => this.callback({ type: 'showList' })
+          })
+          
+          /**
+            * deselect on selecting a point
+            */
+          this.callback({ type: 'paintingEnd' })
+        }
+      }),
+
+      /**
+       * translate point
+       */
+      this.dragHoveredAnnotationsDelta$.subscribe(ev => {
+        const { ann, deltaX, deltaY, deltaZ } = ev
+        const { pickedAnnotationId } = ann.detail
+        const foundAnn = this.managedAnnotations.find(ann => ann.id === pickedAnnotationId)
+        if (foundAnn) {
+          foundAnn.translate(deltaX, deltaY, deltaZ)
+          this.managedAnnotations$.next(this.managedAnnotations)
+        }
+      }),
+    )
+  }
+
+  addAnnotation(point: Point){
+    const found = this.managedAnnotations.find(p => p.id === point.id)
+    if (found) throw new Error(`Point annotation already added`)
+    point.remove = () => this.removeAnnotation(point.id)
+    this.managedAnnotations.push(point)
+    this.managedAnnotations$.next(this.managedAnnotations)
+  }
+
+  /**
+   * @description remove managed annotation via id
+   * @param id id of annotation
+   */
+  removeAnnotation(id: string) {
+    const idx = this.managedAnnotations.findIndex(ann => id === ann.id)
+    if (idx < 0){
+      throw new Error(`cannot find point idx ${idx}`)
+      return
+    }
+    this.managedAnnotations.splice(idx, 1)
+    this.managedAnnotations$.next(this.managedAnnotations)
+  }
+
+  onMouseMoveRenderPreview(pos: [number, number, number]) {
+    return [{
+      id: `${ToolPoint.PREVIEW_ID}_0`,
+      point: pos,
+      type: 'point',
+      description: ''
+    }] as INgAnnotationTypes['point'][]
+  }
+
+  ngOnDestroy(){
+    while (this.subs.length > 0) this.subs.pop().unsubscribe()
+  }
+}
diff --git a/src/atlasComponents/userAnnotations/tools/point/point.component.ts b/src/atlasComponents/userAnnotations/tools/point/point.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c28152125a1a006ac1f69d8543792364dcb8b4ab
--- /dev/null
+++ b/src/atlasComponents/userAnnotations/tools/point/point.component.ts
@@ -0,0 +1,71 @@
+import { Component, ElementRef, Inject, Input, OnDestroy, Optional, ViewChild } from "@angular/core";
+import { Point, POINT_ICON_CLASS } from "../point";
+import { IAnnotationGeometry, TExportFormats, UDPATE_ANNOTATION_TOKEN } from "../type";
+import { ToolCmpBase } from "../toolCmp.base";
+import { Store } from "@ngrx/store";
+import { viewerStateChangeNavigation } from "src/services/state/viewerState/actions";
+import { ComponentStore } from "src/viewerModule/componentStore";
+import { ARIA_LABELS } from 'common/constants'
+
+@Component({
+  selector: 'point-update-cmp',
+  templateUrl: './point.template.html',
+  styleUrls: [
+    './point.style.css',
+  ]
+})
+
+export class PointUpdateCmp extends ToolCmpBase implements OnDestroy{
+
+  public ARIA_LABELS = ARIA_LABELS
+  public POINT_ICON_CLASS = POINT_ICON_CLASS
+
+  @Input('update-annotation')
+  updateAnnotation: Point
+
+  @ViewChild('copyTarget', { read: ElementRef, static: false })
+  copyTarget: ElementRef
+
+  constructor(
+    private store: Store<any>,
+    cStore: ComponentStore<{ useFormat: TExportFormats }>,
+    @Optional() @Inject(UDPATE_ANNOTATION_TOKEN) updateAnnotation: IAnnotationGeometry,
+  ){
+    super(cStore)
+    
+    if (updateAnnotation) {
+      if (updateAnnotation instanceof Point) {
+        this.updateAnnotation = updateAnnotation
+      }
+    }
+  }
+
+  ngOnDestroy(){
+    while (this.sub.length > 0) this.sub.pop().unsubscribe()
+  }
+
+  get copyValue(){
+    return this.copyTarget && this.copyTarget.nativeElement.value
+  }
+
+  gotoRoi(){
+
+    if (!this.updateAnnotation) {
+      throw new Error(`updateAnnotation undefined`)
+    }
+    const { x, y, z } = this.updateAnnotation
+    this.store.dispatch(
+      viewerStateChangeNavigation({
+        navigation: {
+          position: [x, y, z],
+          positionReal: true,
+          animation: {}
+        }
+      })
+    )
+  }
+
+  remove(){
+    this.updateAnnotation?.remove()
+  }
+}
diff --git a/src/ui/templateParcellationCitations/templateParcellationCitations.style.css b/src/atlasComponents/userAnnotations/tools/point/point.style.css
similarity index 100%
rename from src/ui/templateParcellationCitations/templateParcellationCitations.style.css
rename to src/atlasComponents/userAnnotations/tools/point/point.style.css
diff --git a/src/atlasComponents/userAnnotations/tools/point/point.template.html b/src/atlasComponents/userAnnotations/tools/point/point.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..b1b5ab285e11bc2926f3a00d4b8b0b39b4ab0579
--- /dev/null
+++ b/src/atlasComponents/userAnnotations/tools/point/point.template.html
@@ -0,0 +1,51 @@
+<!-- summary of geometry -->
+
+<span class="m-2 text-muted">
+  Point
+</span>
+
+<mat-chip-list>
+  <mat-chip [matTooltip]="updateAnnotation"
+    (click)="gotoRoi()">
+    0
+  </mat-chip>
+</mat-chip-list>
+
+<mat-divider class="m-2 d-block position-relative"></mat-divider>
+
+<!-- actions -->
+
+<div class="d-flex">
+
+  <!-- export -->
+  <button mat-icon-button
+    [attr.aria-label]="ARIA_LABELS.USER_ANNOTATION_EXPORT_SINGLE"
+    [matTooltip]="ARIA_LABELS.USER_ANNOTATION_EXPORT_SINGLE"
+    [matMenuTriggerFor]="exportMenu">
+    <i class="fas fa-file-export"></i>
+  </button>
+
+  <!-- delete -->
+  <button mat-icon-button
+    [attr.aria-label]="ARIA_LABELS.USER_ANNOTATION_DELETE"
+    [matTooltip]="ARIA_LABELS.USER_ANNOTATION_DELETE"
+    (click)="remove()">
+    <i class="fas fa-trash"></i>
+  </button>
+</div>
+
+<mat-menu #exportMenu="matMenu" xPosition="before">
+  <div class="iv-custom-comp card text"
+    iav-stop="click">
+
+    <div class="iv-custom-comp text">
+
+      <textarea-copy-export
+        [textarea-copy-export-label]="useFormat"
+        [textarea-copy-export-text]="updateAnnotation.updateSignal$ | async | toFormattedStringPipe : updateAnnotation : useFormat"
+        [textarea-copy-export-download-filename]="updateAnnotation.id + '.sands.json'"
+        [textarea-copy-export-disable]="true">
+      </textarea-copy-export>
+    </div>
+  </div>
+</mat-menu>
diff --git a/src/atlasComponents/userAnnotations/tools/poly.ts b/src/atlasComponents/userAnnotations/tools/poly.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c33662c2afc022ae8240884c629946587057b662
--- /dev/null
+++ b/src/atlasComponents/userAnnotations/tools/poly.ts
@@ -0,0 +1,426 @@
+import { IAnnotationTools, IAnnotationGeometry, TAnnotationEvent, IAnnotationEvents, AbsToolClass, INgAnnotationTypes, TNgAnnotationEv, TToolType, TBaseAnnotationGeomtrySpec, TSandsPolyLine, getCoord, TCallbackFunction } from "./type";
+import { Point, TPointJsonSpec } from './point'
+import { OnDestroy } from "@angular/core";
+import { merge, Observable, Subject, Subscription } from "rxjs";
+import { filter, switchMapTo, takeUntil, withLatestFrom } from "rxjs/operators";
+import { getUuid } from "src/util/fn";
+
+export type TPolyJsonSpec = {
+  points: (TPointJsonSpec|Point)[]
+  edges: [number, number][]
+  '@type': 'siibra-ex/annotation/polyline'
+} & TBaseAnnotationGeomtrySpec
+
+export class Polygon extends IAnnotationGeometry{
+  public id: string
+
+  public points: Point[] = []
+  public edges: [number, number][] = []
+
+  public hasPoint(p: Point): boolean {
+    return this.points.indexOf(p) >= 0
+  }
+
+  private ptWkMp = new WeakMap<Point, {
+    onremove: Function
+  }>()
+
+  public removePoint(p: Point) {
+    if (!this.hasPoint(p)) throw new Error(`polygon does not have this point`)
+    const returnObj = this.ptWkMp.get(p)
+    if (returnObj && returnObj.onremove) returnObj.onremove()
+
+    /**
+     * remove all edges associated with this point
+     */
+    const ptIdx = this.points.indexOf(p)
+    this.edges = this.edges.filter(([ idx1, idx2 ]) => idx1 !== ptIdx && idx2 !== ptIdx)
+    this.points.splice(ptIdx, 1)
+
+    this.sendUpdateSignal()
+  }
+
+  public addPoint(p: Point | {x: number, y: number, z: number}, linkTo?: Point): Point {
+    if (linkTo && !this.hasPoint(linkTo)) {
+      throw new Error(`linkTo point does not exist for polygon!`)
+    }
+    const pointToBeAdded = p instanceof Point
+      ? p
+      : new Point({
+        id: `${this.id}_${getUuid()}`,
+        space: this.space,
+        '@type': 'siibra-ex/annotation/point',
+        ...p
+      })
+    
+    if (!this.hasPoint(pointToBeAdded)) {
+      this.points.push(pointToBeAdded)
+      const sub = pointToBeAdded.updateSignal$.subscribe(
+        () => this.sendUpdateSignal()
+      )
+      this.ptWkMp.set(pointToBeAdded, {
+        onremove: () => {
+          sub.unsubscribe()
+        }
+      })
+    }
+    if (linkTo) {
+      const newEdge = [
+        this.points.indexOf(linkTo),
+        this.points.indexOf(pointToBeAdded)
+      ] as [number, number]
+      this.edges.push(newEdge)
+    }
+    this.sendUpdateSignal()
+    return pointToBeAdded
+  }
+
+  toJSON(): TPolyJsonSpec{
+    const { id, points, edges, space, name, desc } = this
+    return {
+      id,
+      points: points.map(p => p.toJSON()),
+      edges,
+      space,
+      name,
+      desc,
+      '@type': 'siibra-ex/annotation/polyline'
+    }
+  }
+
+  toString() {
+    return `Points: ${JSON.stringify(this.points.map(p => p.toString()))}, edges: ${JSON.stringify(this.edges)}.`
+  }
+
+  toSands(): TSandsPolyLine{
+    return {
+      "@id": this.id,
+      "@type": 'tmp/poly',
+      coordinateSpace: {
+        '@id': this.space["@id"],
+      },
+      coordinates: this.points.map(p => {
+        const { x, y, z } = p
+        return [getCoord(x/1e6), getCoord(y/1e6), getCoord(z/1e6)]
+      }),
+      closed: true
+    }
+  }
+
+  private getNgAnnotationId(edgeIdx: number){
+    return `${this.id}_${edgeIdx}_0`
+  }
+  getNgAnnotationIds(){
+    return this.edges.map((_, edgeIdx) => this.getNgAnnotationId(edgeIdx))
+  }
+  toNgAnnotation(): INgAnnotationTypes['line'][]{
+    return this.edges.map((indices, edgeIdx) => {
+      const pt1 = this.points[indices[0]]
+      const pt2 = this.points[indices[1]]
+      return {
+        id: this.getNgAnnotationId(edgeIdx),
+        pointA: [pt1.x, pt1.y, pt1.z],
+        pointB: [pt2.x, pt2.y, pt2.z],
+        type: 'line',
+        description: ''
+      }
+    })
+  }
+
+  parseNgAnnotationObj(pickedAnnotationId: string, pickedOffset: number): { edge: [number, number], edgeIdx: number, point: Point, pointIdx: number } {
+    const [ id, edgeIdx, _shouldBeZero ] = pickedAnnotationId.split('_')
+    if (id !== this.id) return null
+
+    if (pickedOffset === 0) {
+      // focus === edge
+
+      const edgeIdxNumber = Number(edgeIdx)
+      return {
+        edgeIdx: edgeIdxNumber,
+        edge: this.edges[edgeIdxNumber],
+        pointIdx: null,
+        point: null
+      }
+    }
+    if (pickedOffset > 2) throw new Error(`polygon should not have picked offset > 2, but is ${pickedOffset}`)
+    const edgeIdxNumber = Number(edgeIdx)
+    const edge = this.edges[edgeIdxNumber]
+    const pointIdx = edge[ pickedOffset - 1 ]
+    return {
+      edgeIdx: edgeIdxNumber,
+      edge,
+      pointIdx,
+      point: this.points[pointIdx]
+    }
+  }
+
+  static fromJSON(json: TPolyJsonSpec){
+    return new Polygon(json)
+  }
+
+  static fromSANDS(sands: TSandsPolyLine): Polygon {
+    const {
+      "@id": id,
+      "@type": type,
+      coordinateSpace,
+      coordinates
+    } = sands
+    if (type === 'tmp/poly') {
+      const points: Point[] = []
+      const edges: [number, number][] = []
+      for (const coordinate of coordinates) {
+        const parsedValue = coordinate.map(c => {
+          if (c.unit["@id"] !== 'id.link/mm') throw new Error(`Unit does not parse`)
+          return c.value * 1e6
+        })
+        const p = new Point({
+          space: coordinateSpace,
+          x: parsedValue[0],
+          y: parsedValue[1],
+          z:  parsedValue[2],
+          "@type": "siibra-ex/annotation/point"
+        })
+        const newIdx = points.push(p)
+        if (newIdx > 1) {
+          edges.push([ newIdx - 2, newIdx - 1 ])
+        }
+      }
+
+      const poly = new Polygon({
+        id,
+        "@type": 'siibra-ex/annotation/polyline',
+        space: coordinateSpace,
+        points,
+        edges
+      })
+      return poly
+    }
+
+    throw new Error(`cannot import sands`)
+  }
+
+  constructor(spec?: TPolyJsonSpec){
+    super(spec)
+    const { points = [], edges = [] } = spec || {}
+    this.points = points.map(p => {
+      if (p instanceof Point) return p
+      return Point.fromJSON(p)
+    })
+    this.edges = edges
+  }
+
+  private sendUpdateSignal(){
+    this.updateSignal$.next(this.toString())
+  }
+
+  public translate(x: number, y: number, z: number) {
+    for (const p of this.points){
+      p.translate(x, y, z)
+    }
+    this.sendUpdateSignal()
+  }
+}
+
+export const POLY_ICON_CLASS = 'fas fa-draw-polygon'
+
+export class ToolPolygon extends AbsToolClass<Polygon> implements IAnnotationTools, OnDestroy {
+  static PREVIEW_ID='tool_poly_preview'
+
+  public name = 'polygon'
+  public iconClass = POLY_ICON_CLASS
+  public toolType: TToolType = 'drawing'
+
+  private selectedPoly: Polygon
+  private lastAddedPoint: Point
+
+  private managedAnnotations: Polygon[] = []
+  public managedAnnotations$ = new Subject<Polygon[]>()
+
+  public subs: Subscription[] = []
+
+  onMouseMoveRenderPreview(pos: [number, number, number]) {
+    if (this.lastAddedPoint) {
+      const { x, y, z } = this.lastAddedPoint
+      return [{
+        id: `${ToolPolygon.PREVIEW_ID}_0`,
+        pointA: [ x, y, z ],
+        pointB: pos,
+        type: 'line',
+        description: ''
+      }] as INgAnnotationTypes['line'][]
+    }
+    return [{
+      id: `${ToolPolygon.PREVIEW_ID}_0`,
+      point: pos,
+      type: 'point',
+      description: ''
+    }] as INgAnnotationTypes['point'][]
+  }
+
+  constructor(
+    annotationEv$: Observable<TAnnotationEvent<keyof IAnnotationEvents>>,
+    callback: TCallbackFunction
+  ){
+    super(annotationEv$, callback)
+    this.init()
+    const toolDeselect$ = this.toolSelected$.pipe(
+      filter(flag => !flag)
+    )
+    const toolSelThenClick$ = this.toolSelected$.pipe(
+      filter(flag => !!flag),
+      switchMapTo(this.mouseClick$.pipe(
+        takeUntil(toolDeselect$)
+      ))
+    )
+
+    this.subs.push(
+      this.metadataEv$.subscribe(ev => {
+        this.space = ev.detail.space
+      }),
+
+      /**
+       * on end tool select
+       */
+      toolDeselect$.subscribe(() => {
+
+        /**
+         * cleanup poly on tool deselect
+         */
+        if (this.selectedPoly) {
+
+          const { edges, points } = this.selectedPoly
+          /**
+           * check if closed. if not close, close it
+           */
+          if (edges.length > 0) {
+
+            if (edges[edges.length - 1].every(v => v !== 0)) {
+              this.selectedPoly.addPoint(
+                points[0],
+                points[points.length - 1]
+              )
+            }
+          }
+
+          /**
+           * if edges < 3, discard poly
+           */
+          if (edges.length < 3) {
+            this.removeAnnotation(this.selectedPoly.id)
+          }
+        }
+
+        this.managedAnnotations$.next(this.managedAnnotations)
+
+        this.selectedPoly = null
+        this.lastAddedPoint = null
+      }),
+      /**
+       * on tool selected
+       * on mouse down
+       * until tool deselected
+       */
+      toolSelThenClick$.pipe(
+        withLatestFrom(this.hoverAnnotation$)
+      ).subscribe(([mouseev, ann]) => {
+        if (!this.selectedPoly) {
+          const newPoly =  new Polygon({
+            edges: [],
+            points: [],
+            space: this.space,
+            '@type': 'siibra-ex/annotation/polyline'
+          })
+          this.addAnnotation(newPoly)
+          this.selectedPoly = newPoly
+        } else {
+
+          if (ann.detail) {
+            const { pickedAnnotationId, pickedOffset } = ann.detail
+            const out = this.selectedPoly.parseNgAnnotationObj(pickedAnnotationId, pickedOffset)
+            const isFirstPoint = out?.point === this.selectedPoly.points[0]
+            if (isFirstPoint) {
+              this.selectedPoly.addPoint(
+                this.selectedPoly.points[0],
+                this.lastAddedPoint
+              )
+
+              if (this.callback) {
+                this.callback({
+                  type: 'message',
+                  message: 'Polyline added.',
+                  action: 'Open',
+                  actionCallback: () => this.callback({ type: 'showList' })
+                })
+                this.callback({
+                  type: 'paintingEnd',
+                })
+              }
+              return
+            }
+          }
+  
+        }
+
+        const addedPoint = this.selectedPoly.addPoint(
+          mouseev.detail.ngMouseEvent,
+          this.lastAddedPoint
+        )
+        this.lastAddedPoint = addedPoint
+
+        /**
+         * always emit new annotation onclick
+         */
+        this.managedAnnotations$.next(this.managedAnnotations)
+      }),
+
+      /**
+       * translate point when on hover a point
+       * translate entire annotation when hover edge
+       */
+      this.dragHoveredAnnotationsDelta$.subscribe(val => {
+        const { ann, deltaX, deltaY, deltaZ } = val
+        const { pickedAnnotationId, pickedOffset } = ann.detail
+        const annotation = this.managedAnnotations.find(poly => poly.parseNgAnnotationObj(pickedAnnotationId, pickedOffset))
+        if (!annotation) {
+          return null
+        }
+        const parsedAnnotation = annotation.parseNgAnnotationObj(pickedAnnotationId, pickedOffset)
+        
+        if (!parsedAnnotation.point) {
+          /**
+           * if point is undefined, then, must be hovering an edge. translate all points
+           */
+          annotation.translate(deltaX, deltaY, deltaZ)
+        } else {
+          /**
+           * else, only translate the point
+           */
+          parsedAnnotation.point.translate(deltaX, deltaY, deltaZ)
+        }
+
+        this.managedAnnotations$.next(this.managedAnnotations)
+      }),
+    )
+  }
+
+  addAnnotation(poly: Polygon){
+    const idx = this.managedAnnotations.findIndex(ann => ann.id === poly.id)
+    if (idx >= 0) throw new Error(`Polygon already added.`)
+    poly.remove = () => this.removeAnnotation(poly.id)
+    this.managedAnnotations.push(poly)
+    this.managedAnnotations$.next(this.managedAnnotations)
+  }
+
+  removeAnnotation(id: string) {
+    const idx = this.managedAnnotations.findIndex(ann => ann.id === id)
+    if (idx < 0) {
+      return
+    }
+    this.managedAnnotations.splice(idx, 1)
+    this.managedAnnotations$.next(this.managedAnnotations)
+  }
+
+  ngOnDestroy(){
+    if (this.subs.length > 0) this.subs.pop().unsubscribe()
+  }
+}
diff --git a/src/atlasComponents/userAnnotations/tools/poly/poly.component.ts b/src/atlasComponents/userAnnotations/tools/poly/poly.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9d8371b404a5eec8d4bc4ca60bbcf07cdfac2c5e
--- /dev/null
+++ b/src/atlasComponents/userAnnotations/tools/poly/poly.component.ts
@@ -0,0 +1,104 @@
+import { Component, ElementRef, Inject, Input, OnDestroy, Optional, ViewChild } from "@angular/core";
+import { MatSnackBar } from "@angular/material/snack-bar";
+import { Polygon, POLY_ICON_CLASS } from "../poly";
+import { ToolCmpBase } from "../toolCmp.base";
+import { IAnnotationGeometry, TExportFormats, UDPATE_ANNOTATION_TOKEN } from "../type";
+import { viewerStateChangeNavigation } from "src/services/state/viewerState/actions";
+import { Store } from "@ngrx/store";
+import { Point } from "../point";
+import { ARIA_LABELS } from 'common/constants'
+import { ComponentStore } from "src/viewerModule/componentStore";
+
+@Component({
+  selector: 'poly-update-cmp',
+  templateUrl: './poly.template.html',
+  styleUrls: [
+    './poly.style.css',
+  ]
+})
+
+export class PolyUpdateCmp extends ToolCmpBase implements OnDestroy{
+  @Input('update-annotation')
+  public updateAnnotation: Polygon
+
+  public ARIA_LABELS = ARIA_LABELS
+  public POLY_ICON_CLASS = POLY_ICON_CLASS
+
+  @ViewChild('copyTarget', { read: ElementRef, static: false })
+  copyTarget: ElementRef
+
+  public useFormat: TExportFormats = 'string'
+
+  constructor(
+    private store: Store<any>,
+    private snackbar: MatSnackBar,
+    cStore: ComponentStore<{ useFormat: TExportFormats }>,
+    @Optional() @Inject(UDPATE_ANNOTATION_TOKEN) updateAnnotation: IAnnotationGeometry,
+  ){
+    super(cStore)
+    if (this.cStore) {
+      this.sub.push(
+        this.cStore.select(store => store.useFormat).subscribe((val: TExportFormats) => {
+          this.useFormat = val
+        })
+      )
+    }
+
+    if (updateAnnotation) {
+      if (updateAnnotation instanceof Polygon) {
+        this.updateAnnotation = updateAnnotation
+      }
+    }
+  }
+
+  ngOnDestroy(){
+    while (this.sub.length > 0) this.sub.pop().unsubscribe()
+  }
+
+  get copyValue(){
+    return this.copyTarget && this.copyTarget.nativeElement.value
+  }
+
+  gotoRoi(roi?: IAnnotationGeometry){
+    if (!this.updateAnnotation) {
+      throw new Error(`updateAnnotation undefined`)
+    }
+
+    if (roi && roi instanceof Point) {
+      const { x, y, z } = roi
+
+      this.store.dispatch(
+        viewerStateChangeNavigation({
+          navigation: {
+            position: [x, y, z],
+            positionReal: true,
+            animation: {}
+          }
+        })
+      )
+      return
+    }
+
+    if (this.updateAnnotation.points.length < 1) {
+      this.snackbar.open('No points added to polygon yet.', 'Dismiss', {
+        duration: 3000
+      })
+      return
+    }
+    const { x, y, z } = this.updateAnnotation.points[0]
+    
+    this.store.dispatch(
+      viewerStateChangeNavigation({
+        navigation: {
+          position: [x, y, z],
+          positionReal: true,
+          animation: {}
+        }
+      })
+    )
+  }
+
+  remove(){
+    this.updateAnnotation?.remove()
+  }
+}
diff --git a/src/atlasComponents/userAnnotations/tools/poly/poly.style.css b/src/atlasComponents/userAnnotations/tools/poly/poly.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..35465c66c929b4586f68c6121822f345cdb8ea21
--- /dev/null
+++ b/src/atlasComponents/userAnnotations/tools/poly/poly.style.css
@@ -0,0 +1,10 @@
+point-update-cmp
+{
+  width: 100%;
+  display: block;
+}
+
+:host >>> .mat-chip-list-wrapper
+{
+  flex-wrap: wrap;
+}
diff --git a/src/atlasComponents/userAnnotations/tools/poly/poly.template.html b/src/atlasComponents/userAnnotations/tools/poly/poly.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..ea8665437974b0c1d29b31de294b72ed426f7095
--- /dev/null
+++ b/src/atlasComponents/userAnnotations/tools/poly/poly.template.html
@@ -0,0 +1,51 @@
+<!-- summary of geometry -->
+
+<span class="m-2 text-muted">
+  Vertices
+</span>
+
+<mat-chip-list>
+  <mat-chip *ngFor="let point of (updateAnnotation?.points || []); let i = index"
+    (click)="gotoRoi(point)"
+    [matTooltip]="point">
+    {{ i }}
+  </mat-chip>
+</mat-chip-list>
+
+<mat-divider class="m-2 d-block position-relative"></mat-divider>
+
+<!-- actions -->
+
+<div class="d-flex">
+
+  <!-- export -->
+  <button mat-icon-button
+    [attr.aria-label]="ARIA_LABELS.USER_ANNOTATION_EXPORT_SINGLE"
+    [matTooltip]="ARIA_LABELS.USER_ANNOTATION_EXPORT_SINGLE"
+    [matMenuTriggerFor]="exportMenu">
+    <i class="fas fa-file-export"></i>
+  </button>
+  
+  <!-- delete -->
+  <button mat-icon-button
+    [attr.aria-label]="ARIA_LABELS.USER_ANNOTATION_DELETE"
+    [matTooltip]="ARIA_LABELS.USER_ANNOTATION_DELETE"
+    (click)="remove()">
+    <i class="fas fa-trash"></i>
+  </button>
+</div>
+
+<mat-menu #exportMenu="matMenu" xPosition="before">
+  <div class="iv-custom-comp card text"
+    iav-stop="click">
+
+    <div class="iv-custom-comp text">
+      <textarea-copy-export
+        [textarea-copy-export-label]="useFormat"
+        [textarea-copy-export-text]="updateAnnotation.updateSignal$ | async | toFormattedStringPipe : updateAnnotation : useFormat"
+        [textarea-copy-export-download-filename]="updateAnnotation.id + '.sands.json'"
+        [textarea-copy-export-disable]="true">
+      </textarea-copy-export>
+    </div>
+  </div>
+</mat-menu>
diff --git a/src/atlasComponents/userAnnotations/tools/select.ts b/src/atlasComponents/userAnnotations/tools/select.ts
new file mode 100644
index 0000000000000000000000000000000000000000..38a0b11ba39ec9df2ddbb20b875f01cd48575257
--- /dev/null
+++ b/src/atlasComponents/userAnnotations/tools/select.ts
@@ -0,0 +1,78 @@
+import { OnDestroy } from "@angular/core";
+import { Observable, Subject, Subscription } from "rxjs";
+import { filter } from 'rxjs/operators'
+import { Point } from "./point";
+import { AbsToolClass, IAnnotationEvents, IAnnotationGeometry, IAnnotationTools, TAnnotationEvent, TCallbackFunction, TNgAnnotationPoint, TToolType } from "./type";
+
+export class ToolSelect extends AbsToolClass<Point> implements IAnnotationTools, OnDestroy {
+
+  public subs: Subscription[] = []
+  toolType: TToolType = 'selecting'
+  iconClass = 'fas fa-mouse-pointer'
+  name = 'Select'
+
+  onMouseMoveRenderPreview(){
+    return []
+  }
+
+  // eslint-disable-next-line @typescript-eslint/no-empty-function
+  addAnnotation(){}
+
+  // eslint-disable-next-line @typescript-eslint/no-empty-function
+  removeAnnotation(){}
+
+  managedAnnotations$ = new Subject<Point[]>()
+  constructor(
+    annotationEv$: Observable<TAnnotationEvent<keyof IAnnotationEvents>>,
+    callback: TCallbackFunction
+  ){
+    super(annotationEv$, callback)
+    this.init()
+  }
+
+  private highLightedAnnotation: IAnnotationGeometry
+  private allManAnnotations: IAnnotationGeometry[] = []
+  init(){
+    if (this.callback) {
+      const obs$ = this.callback({ type: 'requestManAnnStream' })
+      if (!obs$) throw new Error(`Error requestManAnnStream`)
+      this.subs.push(
+        /**
+         * Get stream of all managed annotations
+         */
+        obs$.subscribe(manAnn => {
+          this.allManAnnotations = manAnn
+        }),
+
+        /**
+         * on hover ng annotatoin
+         */
+        this.hoverAnnotation$.subscribe(ev => {
+          this.highLightedAnnotation?.setHighlighted(false)
+          const annId = ev?.detail?.pickedAnnotationId
+          if (!annId) return
+          for (const manan of this.allManAnnotations) {
+            if (manan.getNgAnnotationIds().indexOf(annId) >= 0) {
+              manan.setHighlighted(true)
+              this.highLightedAnnotation = manan
+              return
+            }
+          }
+        }),
+
+        /**
+         * on deselect tool
+         */
+        this.toolSelected$.pipe(
+          filter(flag => !flag)
+        ).subscribe(() => {
+          this.highLightedAnnotation?.setHighlighted(false)
+        })
+      )
+    }
+  }
+
+  ngOnDestroy(){
+    while (this.subs.length > 0) this.subs.pop().unsubscribe()
+  }
+}
\ No newline at end of file
diff --git a/src/atlasComponents/userAnnotations/tools/service.ts b/src/atlasComponents/userAnnotations/tools/service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fde183e33b5d8fd56059d857f4c5dfb600502350
--- /dev/null
+++ b/src/atlasComponents/userAnnotations/tools/service.ts
@@ -0,0 +1,675 @@
+import { Injectable, OnDestroy } from "@angular/core";
+import { ARIA_LABELS } from 'common/constants'
+import { Inject, Optional } from "@angular/core";
+import { select, Store } from "@ngrx/store";
+import { BehaviorSubject, combineLatest, fromEvent, merge, Observable, of, Subject, Subscription } from "rxjs";
+import { map, switchMap, filter, shareReplay, pairwise } from "rxjs/operators";
+import { viewerStateSelectedTemplatePureSelector, viewerStateViewerModeSelector } from "src/services/state/viewerState/selectors";
+import { NehubaViewerUnit } from "src/viewerModule/nehuba";
+import { NEHUBA_INSTANCE_INJTKN } from "src/viewerModule/nehuba/util";
+import { AbsToolClass, ANNOTATION_EVENT_INJ_TOKEN, IAnnotationEvents, IAnnotationGeometry, INgAnnotationTypes, INJ_ANNOT_TARGET, TAnnotationEvent, ClassInterface, TCallbackFunction, TSands, TGeometryJson, TNgAnnotationLine, TCallback } from "./type";
+import { switchMapWaitFor } from "src/util/fn";
+import { Polygon } from "./poly";
+import { Line } from "./line";
+import { Point } from "./point";
+import { FilterAnnotationsBySpace } from "../filterAnnotationBySpace.pipe";
+import { retry } from 'common/util'
+import { MatSnackBar } from "@angular/material/snack-bar";
+
+const LOCAL_STORAGE_KEY = 'userAnnotationKey'
+
+const IAV_VOXEL_SIZES_NM = {
+  'minds/core/referencespace/v1.0.0/265d32a0-3d84-40a5-926f-bf89f68212b9': [25000, 25000, 25000],
+  'minds/core/referencespace/v1.0.0/d5717c4a-0fa1-46e6-918c-b8003069ade8': [39062.5, 39062.5, 39062.5],
+  'minds/core/referencespace/v1.0.0/a1655b99-82f1-420f-a3c2-fe80fd4c8588': [21166.666015625, 20000, 21166.666015625],
+  'minds/core/referencespace/v1.0.0/7f39f7be-445b-47c0-9791-e971c0b6d992': [1000000, 1000000, 1000000,],
+  'minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2': [1000000, 1000000, 1000000]
+}
+
+function scanCollapse<T>(){
+  return (src: Observable<{
+    tool: string
+    annotations: T[]
+  }>) => new Observable<T[]>(obs => {
+    const cache: {
+      [key: string]: T[]
+    } = {}
+    src.subscribe({
+      next: val => {
+        const { annotations, tool } = val
+        cache[tool] = annotations
+        const out: T[] = []
+        for (const key in cache) {
+          out.push(...cache[key])
+        }
+        obs.next(out)
+      },
+      complete: obs.complete,
+      error: obs.error
+    })
+  })
+}
+
+@Injectable({
+  providedIn: 'root'
+})
+
+export class ModularUserAnnotationToolService implements OnDestroy{
+
+  private subscription: Subscription[] = []
+
+  static TMP_PREVIEW_ANN_ID = 'tmp_preview_ann_id'
+  static VIEWER_MODE = ARIA_LABELS.VIEWER_MODE_ANNOTATING
+
+  static ANNOTATION_LAYER_NAME = 'modular_tool_layer_name'
+  static USER_ANNOTATION_LAYER_SPEC = {
+    "type": "annotation",
+    "tool": "annotateBoundingBox",
+    "name": ModularUserAnnotationToolService.ANNOTATION_LAYER_NAME,
+    "annotationColor": "#ee00ff",
+    "annotations": [],
+  }
+
+  private previewNgAnnIds: string[] = []
+
+  private ngAnnotationLayer: any
+  private activeToolName: string
+  private forcedAnnotationRefresh$ = new BehaviorSubject(null)
+
+  private selectedTmpl: any
+  private selectedTmpl$ = this.store.pipe(
+    select(viewerStateSelectedTemplatePureSelector),
+  )
+  public moduleAnnotationTypes: {instance: {name: string, iconClass: string, toolSelected$: Observable<boolean>}, onClick: Function}[] = []
+  private managedAnnotationsStream$ = new Subject<{
+    tool: string
+    annotations: IAnnotationGeometry[]
+  }>()
+
+  private managedAnnotations: IAnnotationGeometry[] = []
+  private filterAnnotationBySpacePipe = new FilterAnnotationsBySpace()
+  public managedAnnotations$ = this.managedAnnotationsStream$.pipe(
+    scanCollapse(),
+    shareReplay(1),
+  )
+  public spaceFilteredManagedAnnotations$ = combineLatest([
+    this.selectedTmpl$,
+    this.managedAnnotations$
+  ]).pipe(
+    map(([tmpl, annts]) => {
+      return this.filterAnnotationBySpacePipe.transform(
+        annts,
+        tmpl
+      )
+    })
+  )
+
+  private registeredTools: {
+    name: string
+    iconClass: string
+    toolInstance: AbsToolClass<any>
+    target?: ClassInterface<IAnnotationGeometry>
+    editCmp?: ClassInterface<any>
+    onDestoryCallBack: () => void
+  }[] = []
+  private mousePosReal: [number, number, number]
+
+  public toolEvents = new Subject()
+  private handleToolCallback: TCallbackFunction = arg => {
+    this.toolEvents.next(arg)
+    switch (arg.type) {
+    case 'paintingEnd': {
+      this.deselectTools()
+      return
+    }
+    case 'requestManAnnStream': {
+      return this.managedAnnotations$
+    }
+    case 'message': {
+      const d = (arg as TCallback['message']['callArg'] & { type: any })
+      const { message, actionCallback, action = null } = d
+      this.snackbar.open(message, action, {
+        duration: 3000
+      }).afterDismissed().subscribe(({ dismissedByAction }) => {
+        if (dismissedByAction && actionCallback) actionCallback()
+      })
+      return
+    }
+    case 'showList': {
+      return
+    }
+    }
+  }
+
+  /**
+   * @description register new annotation tool
+   * Some tools (deletion / dragging) may not have target and editCmp 
+   * 
+   * @param {{
+   *   toolCls: ClassInterface<AbsToolClass>
+   *   target?: ClassInterface<IAnnotationGeometry>
+   *   editCmp?: ClassInterface<any>
+   * }} arg 
+   */
+  public registerTool<T extends AbsToolClass<any>>(arg: {
+    toolCls: ClassInterface<T>
+    target?: ClassInterface<IAnnotationGeometry>
+    editCmp?: ClassInterface<any>
+  }): AbsToolClass<any>{
+    const { toolCls: Cls, target, editCmp } = arg
+    const newTool = new Cls(this.annotnEvSubj, arg => this.handleToolCallback(arg)) as T & { ngOnDestroy?: Function }
+    const { name, iconClass, onMouseMoveRenderPreview } = newTool
+    
+    this.moduleAnnotationTypes.push({
+      instance: newTool,
+      onClick: () => {
+        const tool = this.activeToolName === name
+          ? null
+          : name
+        this.activeToolName = tool
+        this.annotnEvSubj.next({
+          type: 'toolSelect',
+          detail: { name: tool }
+        } as TAnnotationEvent<'toolSelect'>)
+      }
+    })
+
+    const toolSubscriptions: Subscription[] = []
+
+    const { managedAnnotations$ } = newTool
+
+    if ( managedAnnotations$ ){
+      toolSubscriptions.push(
+        managedAnnotations$.subscribe(ann => {
+          this.managedAnnotationsStream$.next({
+            annotations: ann,
+            tool: name
+          })
+        })
+      )
+    }
+
+    this.registeredTools.push({
+      name,
+      iconClass,
+      target,
+      editCmp,
+      toolInstance: newTool,
+      onDestoryCallBack: () => {
+        newTool.ngOnDestroy && newTool.ngOnDestroy()
+        this.managedAnnotationsStream$.next({
+          annotations: [],
+          tool: name
+        })
+        while(toolSubscriptions.length > 0) toolSubscriptions.pop().unsubscribe()
+      }
+    })
+
+    return newTool
+  }
+
+  /**
+   *
+   * @description deregister tool. Calls any necessary clean up function
+   * @param name name of the tool to be deregistered
+   * @returns void
+   */
+  private deregisterTool(name: string) {
+    this.moduleAnnotationTypes = this.moduleAnnotationTypes.filter(tool => tool.instance.name !== name)
+    const foundIdx = this.registeredTools.findIndex(spec => spec.name === name)
+    if (foundIdx >= 0) {
+      const tool = this.registeredTools.splice(foundIdx, 1)[0]
+      tool.onDestoryCallBack()
+    }
+  }
+
+  constructor(
+    private store: Store<any>,
+    private snackbar: MatSnackBar,
+    @Inject(INJ_ANNOT_TARGET) annotTarget$: Observable<HTMLElement>,
+    @Inject(ANNOTATION_EVENT_INJ_TOKEN) private annotnEvSubj: Subject<TAnnotationEvent<keyof IAnnotationEvents>>,
+    @Optional() @Inject(NEHUBA_INSTANCE_INJTKN) nehubaViewer$: Observable<NehubaViewerUnit>,
+  ){
+
+    /**
+     * listen to mouse event on nehubaViewer, and emit as TAnnotationEvent
+     */
+    this.subscription.push(
+      annotTarget$.pipe(
+        switchMap(el => {
+          if (!el) return of(null)
+          return merge(...(
+            ['mousedown', 'mouseup', 'mousemove'].map(type =>
+              fromEvent(el, type, { capture: true }).pipe(
+                map((ev: MouseEvent) => {
+                  return {
+                    type: type,
+                    event: ev,
+                  }
+                }),
+                // filter(ev => ev.event.target)
+              )
+            )
+          ))
+        }),
+        filter(v => !!v)
+      ).subscribe(ev => {
+        /**
+         * do not emit if any mousePosReal is NaN
+         */
+        if (!this.mousePosReal || this.mousePosReal.some(p => isNaN(p))) return
+        const payload = {
+          type: ev.type,
+          detail: {
+            event: ev.event,
+            ngMouseEvent: {
+              x: this.mousePosReal[0],
+              y: this.mousePosReal[1],
+              z: this.mousePosReal[2]
+            }
+          }
+        } as TAnnotationEvent<'mousedown' | 'mouseup' | 'mousemove'>
+        this.annotnEvSubj.next(payload)
+      })
+    )
+
+    /**
+     * on new nehubaViewer, unset annotationLayer
+     */
+    this.subscription.push(
+      nehubaViewer$.subscribe(() => {
+        this.ngAnnotationLayer = null
+      })
+    )
+
+    /**
+     * on new nehubaViewer, listen to mouseState
+     */
+    let cb: () => void
+    this.subscription.push(
+      nehubaViewer$.pipe(
+        switchMap(switchMapWaitFor({
+          condition: nv => !!(nv?.nehubaViewer),
+        }))
+      ).subscribe(nehubaViewer => {
+        if (cb) cb()
+        if (nehubaViewer) {
+          const mouseState = nehubaViewer.nehubaViewer.ngviewer.mouseState
+          cb = mouseState.changed.add(() => {
+            const payload: IAnnotationEvents['hoverAnnotation'] = mouseState.active && !!mouseState.pickedAnnotationId
+              ? {
+                pickedAnnotationId: mouseState.pickedAnnotationId,
+                pickedOffset: mouseState.pickedOffset
+              }
+              : null
+            this.annotnEvSubj.next({
+              type: 'hoverAnnotation',
+              detail: payload
+            })
+          })
+        }
+      })
+    )
+
+    /**
+     * get mouse real position
+     */
+    this.subscription.push(
+      nehubaViewer$.pipe(
+        switchMap(v => v?.mousePosInReal$ || of(null))
+      ).subscribe(v => this.mousePosReal = v)
+    )
+
+    /**
+     * on mouse move, render preview annotation
+     */
+    this.subscription.push(
+      this.annotnEvSubj.pipe(
+        filter(ev => ev.type === 'toolSelect')
+      ).pipe(
+        switchMap((toolSelEv: TAnnotationEvent<'toolSelect'>) => {
+          /**
+           * on new tool set (or unset) remove existing preview annotations, if exist
+           */
+          while (this.previewNgAnnIds.length > 0) {
+            this.clearAllPreviewAnnotations()
+          }
+          if (!toolSelEv.detail.name) return of(null)
+          return this.annotnEvSubj.pipe(
+            filter(v => v.type === 'mousemove'),
+            map((ev: TAnnotationEvent<'mousemove'>) => {
+              return {
+                selectedToolName: toolSelEv.detail.name,
+                ngMouseEvent: ev.detail.ngMouseEvent
+              }
+            })
+          )
+        })
+      ).subscribe((ev: {
+        selectedToolName: string
+        ngMouseEvent: {x: number, y: number, z: number}
+      }) => {
+        if (!ev) {
+          this.clearAllPreviewAnnotations()
+          return
+        }
+        const { selectedToolName, ngMouseEvent } = ev
+        const selectedTool = this.registeredTools.find(tool => tool.name === selectedToolName)
+        if (!selectedTool) {
+          console.warn(`cannot find tool ${selectedToolName}`)
+          return
+        }
+        const { toolInstance } = selectedTool
+        const previewNgAnnotation = toolInstance.onMouseMoveRenderPreview
+          ? toolInstance.onMouseMoveRenderPreview([ngMouseEvent.x, ngMouseEvent.y, ngMouseEvent.z])
+          : []
+
+        if (this.previewNgAnnIds.length !== previewNgAnnotation.length) {
+          this.clearAllPreviewAnnotations()
+        }
+        for (let idx = 0; idx < previewNgAnnotation.length; idx ++) {
+          const localAnnotations = this.ngAnnotationLayer.layer.localAnnotations
+          const annSpec = {
+            ...parseNgAnnotation(previewNgAnnotation[idx]),
+            id: `${ModularUserAnnotationToolService.TMP_PREVIEW_ANN_ID}_${idx}`
+          }
+          const annRef = localAnnotations.references.get(annSpec.id)
+          if (annRef) {
+            localAnnotations.update(
+              annRef,
+              annSpec
+            )
+          } else {
+            localAnnotations.add(
+              annSpec
+            )
+          }
+          this.previewNgAnnIds[idx] = annSpec.id
+        }
+      })
+    )
+
+    /**
+     * on tool managed annotations update
+     */
+    const spaceFilteredManagedAnnotationUpdate$ = combineLatest([
+      this.forcedAnnotationRefresh$,
+      this.spaceFilteredManagedAnnotations$.pipe(
+        switchMap(switchMapWaitFor({
+          condition: () => !!this.ngAnnotationLayer,
+          leading: true
+        })),
+      )
+    ]).pipe(
+      map(([_, annts]) => {
+        const out = []
+        for (const ann of annts) {
+          out.push(...ann.toNgAnnotation())
+        }
+        return out
+      }),
+      shareReplay(1),
+    )
+    this.subscription.push(
+      // delete removed annotations
+      spaceFilteredManagedAnnotationUpdate$.pipe(
+        pairwise(),
+        filter(([ oldAnn, newAnn ]) => newAnn.length < oldAnn.length),
+      ).subscribe(([ oldAnn, newAnn ]) => {
+        const newAnnIdSet = new Set(newAnn.map(ann => ann.id))
+        const outs = oldAnn.filter(ann => !newAnnIdSet.has(ann.id))
+        for (const out of outs){
+          this.deleteNgAnnotationById(out.id)
+        }
+      }), 
+      //update annotations
+      spaceFilteredManagedAnnotationUpdate$.subscribe(arr => {
+        const ignoreNgAnnIdsSet = new Set<string>()
+        for (const hiddenAnnot of this.hiddenAnnotations) {
+          const ids = hiddenAnnot.getNgAnnotationIds()
+          for (const id of ids) {
+            ignoreNgAnnIdsSet.add(id)
+          }
+        }
+        for (const annotation of arr) {
+          if (ignoreNgAnnIdsSet.has(annotation.id)) {
+            this.deleteNgAnnotationById(annotation.id)
+            continue
+          }
+          const localAnnotations = this.ngAnnotationLayer.layer.localAnnotations
+          const annRef = localAnnotations.references.get(annotation.id)
+          const annSpec = parseNgAnnotation(annotation)
+          if (annRef) {
+            localAnnotations.update(
+              annRef,
+              annSpec
+            )
+          } else {
+            localAnnotations.add(
+              annSpec
+            )
+          }
+        }
+      })
+    )
+
+    /**
+     * on viewer mode update, either create layer, or show/hide layer
+     */
+    this.subscription.push(
+      store.pipe(
+        select(viewerStateViewerModeSelector)
+      ).subscribe(viewerMode => {
+        if (viewerMode === ModularUserAnnotationToolService.VIEWER_MODE) {
+          if (this.ngAnnotationLayer) this.ngAnnotationLayer.setVisible(true)
+          else {
+            const viewer = (window as any).viewer
+            const voxelSize = IAV_VOXEL_SIZES_NM[this.selectedTmpl.fullId]
+            if (!voxelSize) throw new Error(`voxelSize of ${this.selectedTmpl.fullId} cannot be found!`)
+            const layer = viewer.layerSpecification.getLayer(
+              ModularUserAnnotationToolService.ANNOTATION_LAYER_NAME,
+              {
+                ...ModularUserAnnotationToolService.USER_ANNOTATION_LAYER_SPEC,
+                // since voxel coordinates are no longer defined, so voxel size will always be 1/1/1
+                transform: [
+                  [1, 0, 0, 0],
+                  [0, 1, 0, 0],
+                  [0, 0, 1, 0],
+                  [0, 0, 0, 1],
+                ]
+              }
+            )
+            this.ngAnnotationLayer = viewer.layerManager.addManagedLayer(layer)
+
+            /**
+             * on template changes, the layer gets lost
+             * force redraw annotations if layer needs to be recreated
+             */
+            this.forcedAnnotationRefresh$.next(null)
+          }
+        } else {
+          if (this.ngAnnotationLayer) this.ngAnnotationLayer.setVisible(false)
+        }
+      })
+    )
+
+    /**
+     * on template select, update selectedtmpl
+     * required for metadata in annotation geometry and voxel size
+     */
+    this.subscription.push(
+      this.selectedTmpl$.subscribe(tmpl => {
+        this.selectedTmpl = tmpl
+        this.annotnEvSubj.next({
+          type: 'metadataEv',
+          detail: {
+            space: tmpl && { ['@id']: tmpl['@id'] }
+          }
+        })
+        this.forcedAnnotationRefresh$.next(null)
+      }),
+      this.managedAnnotations$.subscribe(ann => {
+        this.managedAnnotations = ann
+      }),
+    )
+
+    /**
+     * on window unload, save annotation
+     */
+
+    /**
+     * before unload, save annotations
+     */
+    window.addEventListener('beforeunload', () => {
+      this.storeAnnotation(this.managedAnnotations)
+    })
+  }
+
+  /**
+   * ensure that loadStoredAnnotation only gets called once
+   */
+  private loadFlag = false
+  public async loadStoredAnnotations(){
+    if (this.loadFlag) return
+    this.loadFlag = true
+    
+    const encoded = window.localStorage.getItem(LOCAL_STORAGE_KEY)
+    if (!encoded) return []
+    const bin = atob(encoded)
+    
+    await retry(() => {
+      if (!!(window as any).export_nehuba) return true
+      else throw new Error(`export nehuba not yet ready`)
+    }, {
+      timeout: 1000,
+      retries: 10
+    })
+    
+    const { pako } = (window as any).export_nehuba
+    const decoded = pako.inflate(bin, { to: 'string' })
+    const arr = JSON.parse(decoded)
+    const anns: IAnnotationGeometry[] = []
+    for (const obj of arr) {
+      const geometry = this.parseAnnotationObject(obj)
+      anns.push(geometry)
+    }
+    
+    for (const ann of anns) {
+      this.importAnnotation(ann)
+    }
+  }
+
+  private storeAnnotation(anns: IAnnotationGeometry[]){
+    const arr = []
+    for (const ann of anns) {
+      const json = ann.toJSON()
+      arr.push(json)
+    }
+    const stringifiedJSON = JSON.stringify(arr)
+    if (!(window as any).export_nehuba) return
+    const { pako } = (window as any).export_nehuba
+    const compressed = pako.deflate(stringifiedJSON)
+    let out = ''
+    for (const num of compressed) {
+      out += String.fromCharCode(num)
+    }
+    const encoded = btoa(out)
+    window.localStorage.setItem(LOCAL_STORAGE_KEY, encoded)
+  }
+
+  private hiddenAnnotationIds = new Set<string>()
+
+  public hiddenAnnotations$ = new BehaviorSubject<IAnnotationGeometry[]>([])
+  private hiddenAnnotations: IAnnotationGeometry[] = []
+  public toggleAnnotationVisibilityById(id: string){
+    if (this.hiddenAnnotationIds.has(id)) this.hiddenAnnotationIds.delete(id)
+    else this.hiddenAnnotationIds.add(id)
+
+    this.hiddenAnnotations = []
+    for (const id of Array.from(this.hiddenAnnotationIds)) {
+      const found = this.managedAnnotations.find(managedAnn => managedAnn.id === id)
+      if (found) {
+        this.hiddenAnnotations.push(found)
+      }
+    }
+    this.hiddenAnnotations$.next(this.hiddenAnnotations)
+    this.forcedAnnotationRefresh$.next(null)
+  }
+
+  public getEditAnnotationCmp(annotation: IAnnotationGeometry): ClassInterface<any>{
+    const foundTool = this.registeredTools.find(t => t.target && annotation instanceof t.target)
+    return foundTool && foundTool.editCmp
+  }
+
+  private clearAllPreviewAnnotations(){
+    while (this.previewNgAnnIds.length > 0) this.deleteNgAnnotationById(this.previewNgAnnIds.pop())
+  }
+
+  private deleteNgAnnotationById(annId: string) {
+    const localAnnotations = this.ngAnnotationLayer.layer.localAnnotations
+    const annRef = localAnnotations.references.get(annId)
+    if (annRef) {
+      localAnnotations.delete(annRef)
+      localAnnotations.references.delete(annId)
+    }
+  }
+
+  public defaultTool: AbsToolClass<any>
+  public deselectTools(){
+
+    this.activeToolName = null
+    this.annotnEvSubj.next({
+      type: 'toolSelect',
+      detail: {
+        name: this.defaultTool.name || null
+      }
+    })
+  }
+
+  parseAnnotationObject(json: TSands | TGeometryJson): IAnnotationGeometry{
+    if (json['@type'] === 'tmp/poly') {
+      return Polygon.fromSANDS(json)
+    }
+    if (json['@type'] === 'tmp/line') {
+      return Line.fromSANDS(json)
+    }
+    if (json['@type'] === 'https://openminds.ebrains.eu/sands/CoordinatePoint') {
+      return Point.fromSANDS(json)
+    }
+    if (json['@type'] === 'siibra-ex/annotation/point') {
+      return Point.fromJSON(json)
+    }
+    if (json['@type'] === 'siibra-ex/annotation/line') {
+      return Line.fromJSON(json)
+    }
+    if (json['@type'] === 'siibra-ex/annotation/polyline') {
+      return Polygon.fromJSON(json)
+    }
+    throw new Error(`cannot parse annotation object`)
+  }
+
+  importAnnotation(annotationObj: IAnnotationGeometry){
+    for (const tool of this.registeredTools) {
+      const { toolInstance, target } = tool
+      if (!!target && annotationObj instanceof target) {
+        toolInstance.addAnnotation(annotationObj)
+        return
+      }
+    }
+  }
+
+  ngOnDestroy(){
+    while(this.subscription.length > 0) this.subscription.pop().unsubscribe()
+  }
+}
+
+export function parseNgAnnotation(ann: INgAnnotationTypes[keyof INgAnnotationTypes]){
+  let overwritingType = null
+  if (ann.type === 'point') overwritingType = 0
+  if (ann.type === 'line') overwritingType = 1
+  if (overwritingType === null) throw new Error(`overwrite type lookup failed for ${ann.type}`)
+  return {
+    ...ann,
+    type: overwritingType
+  }
+}
diff --git a/src/atlasComponents/userAnnotations/tools/textareaCopyExport/textareaCopyExport.component.ts b/src/atlasComponents/userAnnotations/tools/textareaCopyExport/textareaCopyExport.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a3be425dfd94693bce7f3e959001de9bfd7d2715
--- /dev/null
+++ b/src/atlasComponents/userAnnotations/tools/textareaCopyExport/textareaCopyExport.component.ts
@@ -0,0 +1,52 @@
+import { Component, Input } from "@angular/core";
+import { MatSnackBar } from "@angular/material/snack-bar";
+import { ARIA_LABELS } from 'common/constants'
+import { Clipboard } from "@angular/cdk/clipboard";
+
+@Component({
+  selector: 'textarea-copy-export',
+  templateUrl: './textareaCopyExport.template.html',
+  styleUrls: [
+    './textareaCopyExport.style.css'
+  ]
+})
+
+export class TextareaCopyExportCmp {
+
+  @Input('textarea-copy-export-label')
+  label: string
+
+  @Input('textarea-copy-export-text')
+  input: string
+
+  @Input('textarea-copy-export-rows')
+  rows: number = 20
+
+  @Input('textarea-copy-export-cols')
+  cols: number = 50
+
+
+  @Input('textarea-copy-export-download-filename')
+  filename: string = 'download.txt'
+
+  @Input('textarea-copy-export-disable')
+  disableFlag: boolean = false
+
+  public ARIA_LABELS = ARIA_LABELS
+
+  constructor(
+    private snackbar: MatSnackBar,
+    private clipboard: Clipboard,
+  ){
+
+  }
+
+  copyToClipboard(value: string){
+    const success = this.clipboard.copy(`${value}`)
+    this.snackbar.open(
+      success ? `Copied to clipboard!` : `Failed to copy URL to clipboard!`,
+      null,
+      { duration: 1000 }
+    )
+  }
+}
diff --git a/src/ui/viewerStateController/viewerStateCMini/viewerStateMini.style.css b/src/atlasComponents/userAnnotations/tools/textareaCopyExport/textareaCopyExport.style.css
similarity index 100%
rename from src/ui/viewerStateController/viewerStateCMini/viewerStateMini.style.css
rename to src/atlasComponents/userAnnotations/tools/textareaCopyExport/textareaCopyExport.style.css
diff --git a/src/atlasComponents/userAnnotations/tools/textareaCopyExport/textareaCopyExport.template.html b/src/atlasComponents/userAnnotations/tools/textareaCopyExport/textareaCopyExport.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..a82f199522da1981e81a01e28358937febec850e
--- /dev/null
+++ b/src/atlasComponents/userAnnotations/tools/textareaCopyExport/textareaCopyExport.template.html
@@ -0,0 +1,32 @@
+<mat-form-field>
+  <mat-label *ngIf="label">
+    {{ label }}
+  </mat-label>
+  <textarea
+    [rows]="rows"
+    [cols]="cols"
+    [disabled]="disableFlag"
+    matInput
+    #exportTarget>{{ input }}</textarea>
+
+  <button mat-icon-button
+    matSuffix
+    iav-stop="click"
+    aria-label="Copy to clipboard"
+    matTooltip="Copy to clipboard."
+    (click)="copyToClipboard(exportTarget.value)"
+    color="basic">
+    <i class="fas fa-copy"></i>
+  </button>
+  <button mat-icon-button
+    matSuffix
+    iav-stop="click"
+    [matTooltip]="ARIA_LABELS.USER_ANNOTATION_EXPORT_SINGLE"
+    [attr.aria-label]="ARIA_LABELS.USER_ANNOTATION_EXPORT_SINGLE"
+    [single-file-output]="{
+      filename: filename,
+      filecontent: exportTarget.value
+    }">
+    <i class="fas fa-download"></i>
+  </button>
+</mat-form-field>
diff --git a/src/atlasComponents/userAnnotations/tools/toFormattedString.pipe.ts b/src/atlasComponents/userAnnotations/tools/toFormattedString.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3fcdf62a3c645d27ea693132e9ef95e0051530c6
--- /dev/null
+++ b/src/atlasComponents/userAnnotations/tools/toFormattedString.pipe.ts
@@ -0,0 +1,22 @@
+import { Pipe, PipeTransform } from "@angular/core";
+import { IAnnotationGeometry, TExportFormats } from "./type";
+
+@Pipe({
+  name: 'toFormattedStringPipe',
+  pure: true
+})
+
+export class ToFormattedStringPipe implements PipeTransform{
+
+  public transform(_: any, annotation: IAnnotationGeometry, format: TExportFormats){
+    if (format === 'json') {
+      return JSON.stringify(annotation.toJSON(), null, 2)
+    }
+
+    if (format === 'sands') {
+      return JSON.stringify(annotation.toSands(), null, 2)
+    }
+
+    return annotation.toString()
+  }
+}
diff --git a/src/atlasComponents/userAnnotations/tools/toolCmp.base.ts b/src/atlasComponents/userAnnotations/tools/toolCmp.base.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6eae6a303fba193dd3b5e254f3e7e56317f5f6a9
--- /dev/null
+++ b/src/atlasComponents/userAnnotations/tools/toolCmp.base.ts
@@ -0,0 +1,43 @@
+import { ARIA_LABELS } from 'common/constants'
+import { ComponentStore } from "src/viewerModule/componentStore";
+import { TExportFormats } from "./type";
+import { Subscription } from "rxjs";
+
+export abstract class ToolCmpBase {
+  public ARIA_LABELS = ARIA_LABELS
+
+  public viableFormats: TExportFormats[] = ['json', 'sands']
+  public useFormat: TExportFormats = 'json'
+
+  protected sub: Subscription[] = []
+  constructor(
+    protected cStore: ComponentStore<{ useFormat: TExportFormats }>,
+  ){
+
+    if (this.cStore) {
+      this.sub.push(
+        this.cStore.select(store => store.useFormat).subscribe((val: TExportFormats) => {
+          this.useFormat = val
+        })
+      )
+    }
+  }
+
+  setFormat(format: TExportFormats){
+    if (this.cStore) {
+      this.cStore.setState({
+        useFormat: format
+      })
+    }
+  }
+
+  /**
+   * Intention of navigating to ROI
+   */
+  abstract gotoRoi(): void
+
+  /**
+   * Intention to remove
+   */
+  abstract remove(): void
+}
diff --git a/src/atlasComponents/userAnnotations/tools/type.ts b/src/atlasComponents/userAnnotations/tools/type.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1716457a166eea87cc6b9943b4c3ebed3538c03a
--- /dev/null
+++ b/src/atlasComponents/userAnnotations/tools/type.ts
@@ -0,0 +1,376 @@
+import { InjectionToken } from "@angular/core"
+import { merge, Observable, of, Subject, Subscription } from "rxjs"
+import { filter, map, mapTo, pairwise, switchMap, switchMapTo, takeUntil, withLatestFrom } from 'rxjs/operators'
+import { getUuid } from "src/util/fn"
+import { TLineJsonSpec } from "./line"
+import { TPointJsonSpec } from "./point"
+import { TPolyJsonSpec } from "./poly"
+
+/**
+ * base class to be extended by all annotation tools
+ * TODO perhaps split into drawing subclass/utility subclass
+ */
+
+export abstract class AbsToolClass<T extends IAnnotationGeometry> {
+
+  public abstract name: string
+  public abstract iconClass: string
+
+  public abstract addAnnotation(annotation: T): void
+  public abstract removeAnnotation(id: string): void
+  public abstract managedAnnotations$: Observable<T[]>
+
+  abstract subs: Subscription[]
+  protected space: TBaseAnnotationGeomtrySpec['space']
+
+  /**
+   * @description to be overwritten by subclass. Called once every mousemove event, if the tool is active.
+   * @param {[number, number, number]} mousepos
+   * @returns {INgAnnotationTypes[keyof INgAnnotationTypes][]} Array of NgAnnotation to be rendered.
+   */
+  public abstract onMouseMoveRenderPreview(mousepos: [number, number, number]): INgAnnotationTypes[keyof INgAnnotationTypes][]
+
+  constructor(
+    protected annotationEv$: Observable<TAnnotationEvent<keyof IAnnotationEvents>>,
+    protected callback?: TCallbackFunction
+  ){
+
+  }
+
+  init(){
+    this.subs.push(
+      this.metadataEv$.subscribe(ev => {
+        this.space = ev.detail.space
+      })
+    )
+  }
+
+  public toolSelected$ = this.annotationEv$.pipe(
+    filter(ev => ev.type === 'toolSelect'),
+    map(ev => (ev as TAnnotationEvent<'toolSelect'>).detail.name === this.name)
+  )
+
+  protected metadataEv$ = this.annotationEv$.pipe(
+    filter(ev => ev.type === 'metadataEv'),
+  ) as Observable<TAnnotationEvent<'metadataEv'>>
+
+  protected mouseDown$ = this.annotationEv$.pipe(
+    filter(ev => ev.type === 'mousedown')
+  ) as Observable<TAnnotationEvent<'mousedown'>>
+
+  protected mouseUp$ = this.annotationEv$.pipe(
+    filter(ev => ev.type === 'mouseup')
+  ) as Observable<TAnnotationEvent<'mouseup'>>
+
+  protected mouseMove$ = this.annotationEv$.pipe(
+    filter(ev => ev.type === 'mousemove')
+  ) as Observable<TAnnotationEvent<'mousemove'>>
+
+  protected mouseClick$ = this.mouseDown$.pipe(
+    switchMap(ev => this.mouseUp$.pipe(
+      takeUntil(this.mouseMove$),
+      mapTo(ev)
+    ))
+  )
+
+  protected hoverAnnotation$ = this.annotationEv$.pipe(
+    filter(ev => ev.type === 'hoverAnnotation')
+  ) as Observable<TAnnotationEvent<'hoverAnnotation'>>
+
+  /**
+   * on mouseover, then drag annotation
+   * use mousedown as obs src, since hoverAnnotation$ is a bit trigger happy
+   * check if there is a hit on mousedown trigger
+   *
+   * if true - stop mousedown propagation, switchmap to mousemove
+   * if false -
+   *
+   */
+  protected dragHoveredAnnotation$: Observable<{
+    startNgX: number
+    startNgY: number
+    startNgZ: number
+    currNgX: number
+    currNgY: number
+    currNgZ: number
+    ann: TAnnotationEvent<"hoverAnnotation">
+  }> = this.mouseDown$.pipe(
+    withLatestFrom(this.hoverAnnotation$),
+    switchMap(([ mousedown, ann ]) => {
+      if (!(ann.detail)) return of(null)
+      const { ngMouseEvent, event } = mousedown.detail
+      event.stopPropagation()
+      const { x: startNgX, y: startNgY, z: startNgZ } = ngMouseEvent
+      return this.mouseMove$.pipe(
+        takeUntil(this.mouseUp$),
+        map(ev => {
+          const { x: currNgX, y: currNgY, z: currNgZ } = ev.detail.ngMouseEvent
+          return {
+            startNgX,
+            startNgY,
+            startNgZ,
+            currNgX,
+            currNgY,
+            currNgZ,
+            ann,
+          }
+        })
+      )
+    }),
+    filter(v => !!v)
+  )
+
+
+  /**
+   * emit on init, and reset on mouseup$
+   * otherwise, pairwise confuses last drag event and first drag event
+   */
+  protected dragHoveredAnnotationsDelta$: Observable<{
+    ann: TAnnotationEvent<"hoverAnnotation">
+    deltaX: number
+    deltaY: number
+    deltaZ: number
+  }> = merge(
+    of(null),
+    this.mouseUp$
+  ).pipe(
+    switchMapTo(this.dragHoveredAnnotation$.pipe(
+      pairwise(),
+      map(([ prev, curr ]) => {
+        const { currNgX, currNgY, currNgZ } = curr
+        const {
+          currNgX: prevNgX,
+          currNgY: prevNgY,
+          currNgZ: prevNgZ
+        } = prev
+        return {
+          ann: curr.ann,
+          deltaX: currNgX - prevNgX,
+          deltaY: currNgY - prevNgY,
+          deltaZ: currNgZ - prevNgZ,
+        }
+      }),
+    ))
+  )
+}
+
+export type TToolType = 'selecting' | 'drawing' | 'deletion'
+
+export type TCallback = {
+  paintingEnd: {
+    callArg: {}
+    returns: void
+  }
+  requestManAnnStream: {
+    callArg: {}
+    returns: Observable<IAnnotationGeometry[]>
+  }
+  message: {
+    callArg: {
+      message: string
+      action?: string
+      actionCallback?: () => void
+    }
+    returns: void
+  }
+  showList: {
+    callArg: {}
+    returns: void
+  }
+}
+
+export type TCallbackFunction = <T extends keyof TCallback>(arg: TCallback[T]['callArg'] & { type: T }) => TCallback[T]['returns'] | void
+
+export type TBaseAnnotationGeomtrySpec = {
+  id?: string
+  space?: {
+    ['@id']: string
+  }
+  name?: string
+  desc?: string
+}
+
+export function getCoord(value: number): TSandsQValue {
+  return {
+    '@id': getUuid(),
+    '@type': "https://openminds.ebrains.eu/core/QuantitativeValue",
+    value,
+    unit: {
+      "@id": 'id.link/mm'
+    }
+  }
+}
+
+type TSandsQValue = {
+  '@id': string
+  '@type': 'https://openminds.ebrains.eu/core/QuantitativeValue'
+  uncertainty?: [number, number]
+  value: number
+  unit: {
+    '@id': 'id.link/mm'
+  }
+}
+type TSandsCoord = [TSandsQValue, TSandsQValue] | [TSandsQValue, TSandsQValue, TSandsQValue]
+
+export type TGeometryJson = TPointJsonSpec | TLineJsonSpec | TPolyJsonSpec
+export type TSands = TSandsPolyLine | TSandsLine | TSandsPoint
+
+export type TSandsPolyLine = {
+  coordinates: TSandsCoord[]
+  closed: boolean
+  coordinateSpace: {
+    '@id': string
+  }
+  '@type': 'tmp/poly'
+  '@id': string
+}
+
+export type TSandsLine = {
+  coordinatesFrom: TSandsCoord
+  coordinatesTo: TSandsCoord
+  coordinateSpace: {
+    '@id': string
+  }
+  '@type': 'tmp/line'
+  '@id': string
+}
+
+export type TSandsPoint = {
+  coordinates: TSandsCoord
+  coordinateSpace: {
+    '@id': string
+  }
+  '@type': 'https://openminds.ebrains.eu/sands/CoordinatePoint'
+  '@id': string
+}
+
+export interface ISandsAnnotation {
+  point: TSandsPoint
+  line: TSandsLine
+  polyline: TSandsPolyLine
+}
+
+export abstract class Highlightable {
+
+  public highlighted = false
+  constructor(defaultFlag?: boolean){
+    if (typeof defaultFlag !== 'undefined') {
+      this.highlighted = defaultFlag
+    }
+  }
+  setHighlighted(flag: boolean){
+    this.highlighted = flag
+  }
+}
+
+export abstract class IAnnotationGeometry extends Highlightable {
+  public id: string
+  
+  public name: string
+  public desc: string
+
+  public space: TBaseAnnotationGeomtrySpec['space']
+
+  abstract getNgAnnotationIds(): string[]
+  abstract toNgAnnotation(): INgAnnotationTypes[keyof INgAnnotationTypes][]
+  abstract toJSON(): object
+  abstract toString(): string
+  abstract toSands(): ISandsAnnotation[keyof ISandsAnnotation]
+
+  public remove() {
+    throw new Error(`The remove method needs to be overwritten by the tool manager`)
+  }
+  public updateSignal$ = new Subject()
+
+  constructor(spec?: TBaseAnnotationGeomtrySpec){
+    super()
+    this.id = spec && spec.id || getUuid()
+    this.space = spec?.space
+    this.name = spec?.name
+    this.desc = spec?.desc
+  }
+
+  setName(name: string) {
+    this.name = name
+    this.updateSignal$.next(this.toString())
+  }
+  setDesc(desc: string) {
+    this.desc = desc
+    this.updateSignal$.next(this.toString())
+  }
+}
+
+export interface IAnnotationTools {
+  name: string
+  iconClass: string
+  toolType: TToolType
+}
+
+export type TNgAnnotationEv = {
+  pickedAnnotationId: string
+  pickedOffset: number
+}
+
+export type TNgMouseEvent = {
+  event: MouseEvent
+  ngMouseEvent: {
+    x: number
+    y: number
+    z: number
+  }
+}
+
+export type TMetaEvent = {
+  space: { ['@id']: string }
+}
+
+export interface IAnnotationEvents {
+  toolSelect: {
+    name: string
+  }
+  mousemove: TNgMouseEvent
+  mousedown: TNgMouseEvent
+  mouseup: TNgMouseEvent
+  hoverAnnotation: TNgAnnotationEv
+
+  metadataEv: TMetaEvent
+}
+
+export type TAnnotationEvent<T extends keyof IAnnotationEvents> = {
+  type: T
+  detail: IAnnotationEvents[T]
+}
+
+export const ANNOTATION_EVENT_INJ_TOKEN = new InjectionToken<
+  Observable<TAnnotationEvent<keyof IAnnotationEvents>>
+>('ANNOTATION_EVENT_INJ_TOKEN')
+
+
+export type TNgAnnotationLine = {
+  type: 'line'
+  pointA: [number, number, number]
+  pointB: [number, number, number]
+  id: string
+  description?: string
+}
+
+export type TNgAnnotationPoint = {
+  type: 'point'
+  point: [number, number, number]
+  id: string
+  description?: string
+}
+
+export interface INgAnnotationTypes {
+  line: TNgAnnotationLine
+  point: TNgAnnotationPoint
+}
+
+export const INJ_ANNOT_TARGET = new InjectionToken<Observable<HTMLElement>>('INJ_ANNOT_TARGET')
+export const UDPATE_ANNOTATION_TOKEN = new InjectionToken<IAnnotationGeometry>('UDPATE_ANNOTATION_TOKEN')
+
+export interface ClassInterface<T> {
+  new (...arg: any[]): T
+}
+
+export type TExportFormats = 'sands' | 'json' | 'string'
diff --git a/src/atlasViewer/atlasViewer.apiService.service.spec.ts b/src/atlasViewer/atlasViewer.apiService.service.spec.ts
index f8dd6f5a8eb71361a90e4dbf07069205808d6235..290bb068a1845621d8f8940d39537d5386b487f6 100644
--- a/src/atlasViewer/atlasViewer.apiService.service.spec.ts
+++ b/src/atlasViewer/atlasViewer.apiService.service.spec.ts
@@ -5,7 +5,7 @@ import { defaultRootState } from "src/services/stateStore.service";
 import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module";
 import { WidgetModule } from 'src/widget';
 import { HttpClientTestingModule, HttpTestingController } from "@angular/common/http/testing";
-import { PluginServices } from "./pluginUnit";
+import { PluginServices } from "src/plugin";
 
 describe('atlasViewer.apiService.service.ts', () => {
 
diff --git a/src/atlasViewer/atlasViewer.apiService.service.ts b/src/atlasViewer/atlasViewer.apiService.service.ts
index 75c5e5a231f25784773cb95a2941ed4692b2c834..dbe633e67022d391826067b9d7d0004e627548bf 100644
--- a/src/atlasViewer/atlasViewer.apiService.service.ts
+++ b/src/atlasViewer/atlasViewer.apiService.service.ts
@@ -6,18 +6,21 @@ import { Observable, Subject, Subscription, from, race, of, } from "rxjs";
 import { distinctUntilChanged, map, filter, startWith, switchMap, catchError, mapTo, take } from "rxjs/operators";
 import { DialogService } from "src/services/dialogService.service";
 import { uiStateMouseOverSegmentsSelector } from "src/services/state/uiState/selectors";
-import { viewerStateFetchedTemplatesSelector } from "src/services/state/viewerState/selectors";
+import {
+  viewerStateFetchedTemplatesSelector,
+  viewerStateViewerModeSelector
+} from "src/services/state/viewerState/selectors";
 import {
   getLabelIndexMap,
   getMultiNgIdsRegionsLabelIndexMap,
   IavRootStoreInterface,
   safeFilter
 } from "src/services/stateStore.service";
-import { FRAGMENT_EMIT_RED } from "src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component";
 import { ClickInterceptor, CLICK_INTERCEPTOR_INJECTOR } from "src/util";
-import { ModalHandler } from "../util/pluginHandlerClasses/modalHandler";
-import { ToastHandler } from "../util/pluginHandlerClasses/toastHandler";
-import { IPluginManifest, PluginServices } from "./pluginUnit";
+import { FRAGMENT_EMIT_RED } from "src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component";
+import { IPluginManifest, PluginServices } from "src/plugin";
+import { ILoadMesh } from 'src/messaging/types'
+import {ARIA_LABELS} from "common/constants";
 
 declare let window
 
@@ -35,14 +38,6 @@ interface IGetUserSelectRegionPr{
 }
 
 export const CANCELLABLE_DIALOG = 'CANCELLABLE_DIALOG'
-export const GET_TOAST_HANDLER_TOKEN = 'GET_TOAST_HANDLER_TOKEN'
-
-export interface ILoadMesh {
-  type: 'VTK'
-  id: string
-  url: string
-}
-export const LOAD_MESH_TOKEN = new InjectionToken<(loadMeshParam: ILoadMesh) => void>('LOAD_MESH_TOKEN')
 
 @Injectable({
   providedIn : 'root'
@@ -79,7 +74,7 @@ export class AtlasViewerAPIServices implements OnDestroy{
   public getNextUserRegionSelectHandler: () => IGetUserSelectRegionPr = () => {
     if (this.getUserToSelectRegion.length > 0) {
       return this.getUserToSelectRegion[this.getUserToSelectRegion.length - 1]
-    } 
+    }
     else return null
   }
 
@@ -87,15 +82,15 @@ export class AtlasViewerAPIServices implements OnDestroy{
     if (this.getUserToSelectRegion.length > 0) {
       this.getUserToSelectRegion.pop()
       this.getUserToSelectRegionUI$.next([...this.getUserToSelectRegion])
-    } 
+    }
   }
 
   private s: Subscription[] = []
 
-  private onMouseClick(ev: any, next){
+  private onMouseClick(ev: any): boolean {
     const { rs, spec } = this.getNextUserRegionSelectHandler() || {}
     if (!!rs) {
-      
+
       let moSegments
       this.store.pipe(
         select(uiStateMouseOverSegmentsSelector),
@@ -121,10 +116,11 @@ export class AtlasViewerAPIServices implements OnDestroy{
                 mousePositionReal = floatArr && Array.from(floatArr).map((val: number) => val / 1e6)
               })
           }
-          return rs({
+          rs({
             type: spec.type,
             payload: mousePositionReal
           })
+          return false
         }
 
         /**
@@ -134,10 +130,11 @@ export class AtlasViewerAPIServices implements OnDestroy{
 
           if (!!moSegments && Array.isArray(moSegments) && moSegments.length > 0) {
             this.popUserRegionSelectHandler()
-            return rs({
+            rs({
               type: spec.type,
               payload: moSegments
             })
+            return false
           }
         }
       } else {
@@ -147,11 +144,12 @@ export class AtlasViewerAPIServices implements OnDestroy{
          */
         if (!!moSegments && Array.isArray(moSegments) && moSegments.length > 0) {
           this.popUserRegionSelectHandler()
-          return rs(moSegments[0])
+          rs(moSegments[0])
+          return false
         }
       }
     }
-    next()
+    return true
   }
 
   constructor(
@@ -161,7 +159,6 @@ export class AtlasViewerAPIServices implements OnDestroy{
     private zone: NgZone,
     private pluginService: PluginServices,
     @Optional() @Inject(CANCELLABLE_DIALOG) openCancellableDialog: (message: string, options: any) => () => void,
-    @Optional() @Inject(GET_TOAST_HANDLER_TOKEN) private getToastHandler: Function,
     @Optional() @Inject(CLICK_INTERCEPTOR_INJECTOR) clickInterceptor: ClickInterceptor
   ) {
     if (clickInterceptor) {
@@ -179,7 +176,7 @@ export class AtlasViewerAPIServices implements OnDestroy{
               this.dismissDialog()
               this.dismissDialog = null
             }
-            
+
             if (arr.length === 0) return of(null)
 
             const last = arr[arr.length - 1]
@@ -255,39 +252,12 @@ export class AtlasViewerAPIServices implements OnDestroy{
       },
       uiHandle : {
         getModalHandler : () => {
-          const handler = new ModalHandler()
-          let modalRef
-          handler.show = () => {
-            /**
-             * TODO enable
-             * temporarily disabled
-             */
-            // modalRef = this.modalService.show(ModalUnit, {
-            //   initialState : {
-            //     title : handler.title,
-            //     body : handler.body
-            //       ? handler.body
-            //       : 'handler.body has yet been defined ...',
-            //     footer : handler.footer
-            //   },
-            //   class : this.darktheme ? 'darktheme' : 'not-darktheme',
-            //   backdrop : handler.dismissable ? true : 'static',
-            //   keyboard : handler.dismissable
-            // })
-          }
-          handler.hide = () => {
-            if (modalRef) {
-              modalRef.hide()
-              modalRef = null
-            }
-          }
-          return handler
+          throw new Error(`uihandle.getModalHandler has been deprecated`)
         },
 
         /* to be overwritten by atlasViewer.component.ts */
         getToastHandler : () => {
-          if (this.getToastHandler) return this.getToastHandler()
-          else throw new Error('getToast Handler not overwritten by atlasViewer.component.ts')
+          throw new Error('uiHandle.getToastHandler has been deprecated')
         },
 
         /**
@@ -381,7 +351,7 @@ export class AtlasViewerAPIServices implements OnDestroy{
     })
 
     this.s.push(
-      this.loadMesh$.subscribe(({ url, id, type }) => {
+      this.loadMesh$.subscribe(({ url, id, type, customFragmentColor = null }) => {
         if (!this.interactiveViewer.viewerHandle) {
           this.snackbar.open('No atlas loaded! Loading mesh failed!', 'Dismiss')
         }
@@ -389,7 +359,7 @@ export class AtlasViewerAPIServices implements OnDestroy{
           [id]: {
             type: 'mesh',
             source: `vtk://${url}`,
-            shader: `void main(){${FRAGMENT_EMIT_RED};}`
+            shader: `void main(){${customFragmentColor || FRAGMENT_EMIT_RED};}`
           }
         })
       })
@@ -416,43 +386,11 @@ export interface IInteractiveViewerInterface {
     datasetsBSubject: Observable<any[]>
   }
 
-  viewerHandle?: {
-    setNavigationLoc: (coordinates: [number, number, number], realSpace?: boolean) => void
-    moveToNavigationLoc: (coordinates: [number, number, number], realSpace?: boolean) => void
-    setNavigationOri: (quat: [number, number, number, number]) => void
-    moveToNavigationOri: (quat: [number, number, number, number]) => void
-    showSegment: (labelIndex: number) => void
-    hideSegment: (labelIndex: number) => void
-    showAllSegments: () => void
-    hideAllSegments: () => void
-
-    // TODO deprecate
-    segmentColourMap: Map<number, {red: number, green: number, blue: number}>
-
-    getLayersSegmentColourMap: () => Map<string, Map<number, {red: number, green: number, blue: number}>>
-
-    // TODO deprecate
-    applyColourMap: (newColourMap: Map<number, {red: number, green: number, blue: number}>) => void
-
-    applyLayersColourMap: (newLayerColourMap: Map<string, Map<number, {red: number, green: number, blue: number}>>) => void
-
-    loadLayer: (layerobj: any) => any
-    removeLayer: (condition: {name: string | RegExp}) => string[]
-    setLayerVisibility: (condition: {name: string|RegExp}, visible: boolean) => void
-
-    add3DLandmarks: (landmarks: IUserLandmark[]) => void
-    remove3DLandmarks: (ids: string[]) => void
-
-    mouseEvent: Observable<{eventName: string, event: MouseEvent}>
-    mouseOverNehuba: Observable<{labelIndex: number, foundRegion: any | null}>
-    mouseOverNehubaLayers: Observable<Array<{layer: {name: string}, segment: any | number }>>
-    mouseOverNehubaUI: Observable<{ segments: any, landmark: any, customLandmark: any }>
-    getNgHash: () => string
-  }
+  viewerHandle?: IVIewerHandle
 
   uiHandle: {
-    getModalHandler: () => ModalHandler
-    getToastHandler: () => ToastHandler
+    getModalHandler: () => void
+    getToastHandler: () => void
     launchNewWidget: (manifest: IPluginManifest) => Promise<any>
     getUserInput: (config: IGetUserInputConfig) => Promise<string>
     getUserConfirmation: (config: IGetUserConfirmation) => Promise<any>
@@ -495,8 +433,39 @@ export interface ICustomRegionSpec{
   type: string // type of EnumCustomRegion
 }
 
-export const API_SERVICE_SET_VIEWER_HANDLE_TOKEN = new InjectionToken<(viewerHandle) => void>('API_SERVICE_SET_VIEWER_HANDLE_TOKEN')
+export interface IVIewerHandle {
+
+  setNavigationLoc: (coordinates: [number, number, number], realSpace?: boolean) => void
+  moveToNavigationLoc: (coordinates: [number, number, number], realSpace?: boolean) => void
+  setNavigationOri: (quat: [number, number, number, number]) => void
+  moveToNavigationOri: (quat: [number, number, number, number]) => void
+  showSegment: (labelIndex: number) => void
+  hideSegment: (labelIndex: number) => void
+  showAllSegments: () => void
+  hideAllSegments: () => void
+
+  getLayersSegmentColourMap: () => Map<string, Map<number, {red: number, green: number, blue: number}>>
+
+  applyLayersColourMap: (newLayerColourMap: Map<string, Map<number, {red: number, green: number, blue: number}>>) => void
+
+  loadLayer: (layerobj: any) => any
+  removeLayer: (condition: {name: string | RegExp}) => string[]
+  setLayerVisibility: (condition: {name: string|RegExp}, visible: boolean) => void
+
+  add3DLandmarks: (landmarks: IUserLandmark[]) => void
+  remove3DLandmarks: (ids: string[]) => void
+
+  mouseEvent: Observable<{eventName: string, event: MouseEvent}>
+  mouseOverNehuba: Observable<{labelIndex: number, foundRegion: any | null}>
+  mouseOverNehubaLayers: Observable<Array<{layer: {name: string}, segment: any | number }>>
+  mouseOverNehubaUI: Observable<{ segments: any, landmark: any, customLandmark: any }>
+  getNgHash: () => string
+}
+
+export type TSetViewerHandle = (viewerHandle: IVIewerHandle) => void
+
+export const API_SERVICE_SET_VIEWER_HANDLE_TOKEN = new InjectionToken<TSetViewerHandle>('API_SERVICE_SET_VIEWER_HANDLE_TOKEN')
 
 export const setViewerHandleFactory = (apiService: AtlasViewerAPIServices) => {
-  return viewerHandle => apiService.interactiveViewer.viewerHandle = viewerHandle
+  return (viewerHandle: IVIewerHandle) => apiService.interactiveViewer.viewerHandle = viewerHandle
 }
diff --git a/src/atlasViewer/atlasViewer.component.ts b/src/atlasViewer/atlasViewer.component.ts
index 8f1410127afa88217bd84cb22b2708121a4c795f..2c406866e5bf53b0005f5af699746c56e02db764 100644
--- a/src/atlasViewer/atlasViewer.component.ts
+++ b/src/atlasViewer/atlasViewer.component.ts
@@ -8,29 +8,25 @@ import {
   TemplateRef,
   ViewChild,
   ElementRef,
-  Inject,
-  Optional,
-  InjectionToken,
 } from "@angular/core";
 import { Store, select, ActionsSubject } from "@ngrx/store";
-import { Observable, Subscription, combineLatest, interval, merge, of, timer, fromEvent } from "rxjs";
+import { Observable, Subscription, interval, merge, of, timer, fromEvent } from "rxjs";
 import { map, filter, distinctUntilChanged, delay, withLatestFrom, switchMapTo, take, startWith } from "rxjs/operators";
 
-import { LayoutMainSide } from "../layouts/mainside/mainside.component";
 import {
   IavRootStoreInterface,
   isDefined,
   safeFilter,
 } from "../services/stateStore.service";
-import { AtlasViewerConstantsServices, UNSUPPORTED_INTERVAL, UNSUPPORTED_PREVIEW } from "./atlasViewer.constantService.service";
+import { UNSUPPORTED_INTERVAL, UNSUPPORTED_PREVIEW } from "src/util/constants";
 import { WidgetServices } from "src/widget";
 
 import { LocalFileService } from "src/services/localFile.service";
-import { AGREE_COOKIE, AGREE_KG_TOS, SHOW_KG_TOS } from "src/services/state/uiState.store";
+import { AGREE_COOKIE, AGREE_KG_TOS } from "src/services/state/uiState.store";
+import { SHOW_KG_TOS } from 'src/services/state/uiState.store.helper'
 import { isSame } from "src/util/fn";
-import { NehubaContainer } from "../ui/nehubaContainer/nehubaContainer.component";
 import { colorAnimation } from "./atlasViewer.animation"
-import { MouseHoverDirective } from "src/atlasViewer/mouseOver.directive";
+import { MouseHoverDirective } from "src/mouseoverModule";
 import {MatSnackBar, MatSnackBarRef} from "@angular/material/snack-bar";
 import {MatDialog, MatDialogRef} from "@angular/material/dialog";
 import { ARIA_LABELS, CONST } from 'common/constants'
@@ -38,13 +34,7 @@ import { ARIA_LABELS, CONST } from 'common/constants'
 import { MIN_REQ_EXPLAINER } from 'src/util/constants'
 import { SlServiceService } from "src/spotlight/sl-service.service";
 import { PureContantService } from "src/util";
-import { viewerStateSetSelectedRegions, viewerStateRemoveAdditionalLayer, viewerStateHelperSelectParcellationWithId } from "src/services/state/viewerState.store.helper";
-import { viewerStateGetOverlayingAdditionalParcellations, viewerStateParcVersionSelector, viewerStateStandAloneVolumes } from "src/services/state/viewerState/selectors";
-import { ngViewerSelectorClearViewEntries } from "src/services/state/ngViewerState/selectors";
-import { ngViewerActionClearView } from "src/services/state/ngViewerState/actions";
-import { uiStateMouseOverSegmentsSelector } from "src/services/state/uiState/selectors";
 import { ClickInterceptorService } from "src/glue";
-import {SET_OVERWRITTEN_COLOR_MAP} from "src/services/state/viewerState.store";
 
 /**
  * TODO
@@ -73,9 +63,6 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit {
   @ViewChild('cookieAgreementComponent', {read: TemplateRef}) public cookieAgreementComponent: TemplateRef<any>
 
   @ViewChild('kgToS', {read: TemplateRef}) public kgTosComponent: TemplateRef<any>
-  @ViewChild(LayoutMainSide) public layoutMainSide: LayoutMainSide
-
-  @ViewChild(NehubaContainer) public nehubaContainer: NehubaContainer
 
   @ViewChild(MouseHoverDirective) private mouseOverNehuba: MouseHoverDirective
 
@@ -88,20 +75,11 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit {
   public sidePanelView$: Observable<string|null>
   private newViewer$: Observable<any>
 
-  public selectedRegions$: Observable<any[]>
-  public selectedPOI$: Observable<any[]>
-
   private snackbarRef: MatSnackBarRef<any>
   public snackbarMessage$: Observable<string>
 
-  public dedicatedView$: Observable<string | null>
-  public onhoverSegments: any[]
-  public onhoverSegments$: Observable<any[]>
-
   public onhoverLandmark$: Observable<{landmarkName: string, datasets: any} | null>
 
-  public overwrittenColorMap$: Observable<any>
-
   private subscriptions: Subscription[] = []
 
   public unsupportedPreviewIdx: number = 0
@@ -109,39 +87,15 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit {
 
   public MIN_REQ_EXPLAINER = MIN_REQ_EXPLAINER
 
-  public isStandaloneVolumes$ = this.store.pipe(
-    select(viewerStateStandAloneVolumes),
-    map(v => v.length > 0)
-  )
-
-  public selectedAdditionalLayers$ = this.store.pipe(
-    select(viewerStateGetOverlayingAdditionalParcellations),
-  )
-
-  public selectedLayerVersions$ = this.store.pipe(
-    select(viewerStateParcVersionSelector),
-    map(arr => arr.map(item => {
-      const overwrittenName = item['@version'] && item['@version']['name']
-      return overwrittenName
-        ? { ...item, displayName: overwrittenName }
-        : item
-    }))
-  )
-
   private selectedParcellation$: Observable<any>
   public selectedParcellation: any
 
   private cookieDialogRef: MatDialogRef<any>
   private kgTosDialogRef: MatDialogRef<any>
 
-  public clearViewKeys$ = this.store.pipe(
-    select(ngViewerSelectorClearViewEntries)
-  )
-
   constructor(
     private store: Store<IavRootStoreInterface>,
     private widgetServices: WidgetServices,
-    private constantsService: AtlasViewerConstantsServices,
     private pureConstantService: PureContantService,
     private matDialog: MatDialog,
     private dispatcher$: ActionsSubject,
@@ -164,60 +118,12 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit {
       map(state => state.focusedSidePanel),
     )
 
-    this.selectedRegions$ = this.store.pipe(
-      select('viewerState'),
-      filter(state => isDefined(state) && isDefined(state.regionsSelected)),
-      map(state => state.regionsSelected),
-      distinctUntilChanged(),
-    )
-
-    this.selectedPOI$ = combineLatest(
-      this.selectedRegions$,
-      this.store.pipe(
-        select('viewerState'),
-        filter(state => isDefined(state) && isDefined(state.landmarksSelected)),
-        map(state => state.landmarksSelected),
-        distinctUntilChanged(),
-      ),
-    ).pipe(
-      map(results => [...results[0], ...results[1]]),
-    )
-
     this.newViewer$ = this.store.pipe(
       select('viewerState'),
       select('templateSelected'),
       distinctUntilChanged(isSame),
     )
 
-    // TODO deprecate
-    this.dedicatedView$ = this.store.pipe(
-      select('viewerState'),
-      select('dedicatedView'),
-      distinctUntilChanged(),
-      map(v => v[v.length -1])
-    )
-
-    // TODO temporary hack. even though the front octant is hidden, it seems if a mesh is present, hover will select the said mesh
-    this.onhoverSegments$ = this.store.pipe(
-      select(uiStateMouseOverSegmentsSelector),
-      filter(v => !!v),
-      distinctUntilChanged((o, n) => o.length === n.length && n.every(segment => o.find(oSegment => oSegment.layer.name === segment.layer.name && oSegment.segment === segment.segment) ) ),
-      /* cannot filter by state, as the template expects a default value, or it will throw ExpressionChangedAfterItHasBeenCheckedError */
-
-    ).pipe(
-      withLatestFrom(
-        this.onhoverLandmark$ || of(null)
-      ),
-      map(([segments, onhoverLandmark]) => onhoverLandmark ? null : segments ),
-      map(segments => {
-        if (!segments) { return null }
-        const filteredSeg = segments.filter(filterFn)
-        return filteredSeg.length > 0
-          ? segments.map(s => s.segment)
-          : null
-      }),
-    )
-
     this.selectedParcellation$ = this.store.pipe(
       select('viewerState'),
       safeFilter('parcellationSelected'),
@@ -232,13 +138,6 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit {
 
     )
 
-    this.overwrittenColorMap$ = this.store.pipe(
-      select('viewerState'),
-      safeFilter('overwrittenColorMap'),
-      map(state => state.overwrittenColorMap),
-      distinctUntilChanged()
-    )
-
     const error = this.el.nativeElement.getAttribute('data-error')
 
     if (error) {
@@ -249,7 +148,6 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit {
 
   public ngOnInit() {
     this.meetsRequirement = this.meetsRequirements()
-    this.clickIntService.addInterceptor(this.selectHoveredRegion.bind(this), true)
 
     if (KIOSK_MODE) {
 
@@ -291,10 +189,6 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit {
       })
     }
 
-    this.subscriptions.push(
-      this.onhoverSegments$.subscribe(seg => this.onhoverSegments = seg)
-    )
-
     this.subscriptions.push(
       this.pureConstantService.useTouchUI$.subscribe(bool => this.ismobile = bool),
     )
@@ -325,13 +219,7 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit {
     )
 
     this.subscriptions.push(
-      this.sidePanelView$.pipe(
-        filter(() => typeof this.layoutMainSide !== 'undefined'),
-      ).subscribe(v => this.layoutMainSide.showSide =  isDefined(v)),
-    )
-
-    this.subscriptions.push(
-      this.constantsService.darktheme$.subscribe(flag => {
+      this.pureConstantService.darktheme$.subscribe(flag => {
         this.rd.setAttribute(document.body, 'darktheme', flag.toString())
       }),
     )
@@ -351,9 +239,9 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit {
       this.rd.appendChild(document.head, prefecthMainBundle)
     }
 
-    this.onhoverLandmark$ = this.mouseOverNehuba.currentOnHoverObs$.pipe(
-      select('landmark')
-    )
+    // this.onhoverLandmark$ = this.mouseOverNehuba.currentOnHoverObs$.pipe(
+    //   select('landmark')
+    // )
 
     /**
      * Show Cookie disclaimer if not yet agreed
@@ -389,62 +277,10 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit {
    */
   public ngOnDestroy() {
     this.subscriptions.forEach(s => s.unsubscribe())
-    this.clickIntService.removeInterceptor(this.selectHoveredRegion.bind(this))
-  }
-
-  private selectHoveredRegion(ev: any, next: Function){
-    if (!this.onhoverSegments) return
-      
-    this.store.dispatch(
-      viewerStateSetSelectedRegions({
-        selectRegions: this.onhoverSegments.slice(0, 1)
-      })
-    )
-    next()
-  }
-
-  public unsetClearViewByKey(key: string){
-    this.store.dispatch(
-      ngViewerActionClearView({ payload: {
-        [key]: false
-      }})
-    )
   }
 
-  public selectParcellation(parc: any) {
-    this.store.dispatch(
-      viewerStateHelperSelectParcellationWithId({
-        payload: parc
-      })
-    )
-  }
-
-  public bindFns(fns){
-    return () => {
-      for (const [ fn, ...arg] of fns) {
-        fn(...arg)
-      }
-    }
-  }
-
-  public clearAdditionalLayer(layer: { ['@id']: string }){
-    this.store.dispatch(
-      viewerStateRemoveAdditionalLayer({
-        payload: layer
-      })
-    )
-  }
-
-  public clearSelectedRegions(){
-    this.store.dispatch(
-      viewerStateSetSelectedRegions({
-        selectRegions: []
-      })
-    )
-  }
-
-  public mouseClickDocument(_event: MouseEvent) {
-    this.clickIntService.run(_event)
+  public mouseClickDocument(event: MouseEvent) {
+    this.clickIntService.callRegFns(event)
   }
 
   /**
@@ -482,6 +318,17 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit {
     })
   }
 
+  public quickTourFinale = {
+    order: 1e6,
+    descriptionMd: `That's it! We hope you enjoy your stay.
+
+---
+
+If you have any comments or need further support, please contact us at [${this.pureConstantService.supportEmailAddress}](mailto:${this.pureConstantService.supportEmailAddress})`,
+    description: `That's it! We hope you enjoy your stay. If you have any comments or need further support, please contact us at ${this.pureConstantService.supportEmailAddress}`,
+    position: 'center'
+  }
+
   @HostBinding('attr.version')
   public _version: string = VERSION
 }
diff --git a/src/atlasViewer/atlasViewer.constantService.service.spec.ts b/src/atlasViewer/atlasViewer.constantService.service.spec.ts
deleted file mode 100644
index e9fe3faeeb0dae335c04d2e0b28d840966b87d5a..0000000000000000000000000000000000000000
--- a/src/atlasViewer/atlasViewer.constantService.service.spec.ts
+++ /dev/null
@@ -1,128 +0,0 @@
-import {} from 'jasmine'
-import { decodeToNumber, encodeNumber } from './atlasViewer.constantService.service'
-
-const FLOAT_PRECISION = 6
-
-describe('encodeNumber/decodeToNumber', () => {
-
-  const getCompareOriginal = (original: number[]) => (element: string, index: number) =>
-    original[index].toString().length >= element.length
-
-  const lengthShortened = (original: number[], encodedString: string[]) =>
-    encodedString.every(getCompareOriginal(original))
-
-  it('should encode/decode positive integer as expected', () => {
-
-    const positiveInt = [
-      0,
-      1,
-      99999999999,
-      12347,
-    ]
-
-    const encodedString = positiveInt.map(n => encodeNumber(n))
-    const decodedString = encodedString.map(s => decodeToNumber(s))
-    expect(decodedString).toEqual(positiveInt)
-
-    expect(lengthShortened(positiveInt, encodedString)).toBe(true)
-  })
-
-  it('should encode/decode ANY positive integer as expected', () => {
-    const posInt = Array(1000).fill(null).map(() => {
-      const numDig = Math.ceil(Math.random() * 7)
-      return Math.floor(Math.random() * Math.pow(10, numDig))
-    })
-    const encodedString = posInt.map(n => encodeNumber(n))
-    const decodedNumber = encodedString.map(s => decodeToNumber(s))
-    expect(decodedNumber).toEqual(posInt)
-
-    expect(lengthShortened(posInt, encodedString)).toBe(true)
-  })
-
-  it('should encode/decode signed integer as expected', () => {
-
-    const signedInt = [
-      0,
-      -0,
-      -1,
-      1,
-      128,
-      -54,
-    ]
-
-    const encodedString = signedInt.map(n => encodeNumber(n))
-    const decodedNumber = encodedString.map(s => decodeToNumber(s))
-
-    /**
-     * -0 will be converted to 0 by the encode/decode process, but does not deep equal, according to jasmine
-     */
-    expect(decodedNumber).toEqual(signedInt.map(v => v === 0 ? 0 : v))
-
-    expect(lengthShortened(signedInt, encodedString)).toBe(true)
-  })
-
-  it('should encode/decode ANY signed integer as expected', () => {
-
-    const signedInt = Array(1000).fill(null).map(() => {
-      const numDig = Math.ceil(Math.random() * 7)
-      return Math.floor(Math.random() * Math.pow(10, numDig)) * (Math.random() > 0.5 ? 1 : -1)
-    })
-    const encodedString = signedInt.map(n => encodeNumber(n))
-    const decodedNumber = encodedString.map(s => decodeToNumber(s))
-
-    /**
-     * -0 will be converted to 0 by the encode/decode process, but does not deep equal, according to jasmine
-     */
-    expect(decodedNumber).toEqual(signedInt.map(v => v === 0 ? 0 : v))
-
-    expect(lengthShortened(signedInt, encodedString)).toBe(true)
-  })
-
-  it('should encode/decode float as expected', () => {
-    const floatNum = [
-      0.111,
-      12.23,
-      1723.0,
-    ]
-
-    const encodedString = floatNum.map(f => encodeNumber(f, { float: true }))
-    const decodedNumber = encodedString.map(s => decodeToNumber(s, { float: true }))
-    expect(decodedNumber.map(n => n.toFixed(FLOAT_PRECISION))).toEqual(floatNum.map(n => n.toFixed(FLOAT_PRECISION)))
-  })
-
-  it('should encode/decode ANY float as expected', () => {
-    const floatNums = Array(1000).fill(null).map(() => {
-      const numDig = Math.ceil(Math.random() * 7)
-      return (Math.random() > 0.5 ? 1 : -1) * Math.floor(
-        Math.random() * Math.pow(10, numDig),
-      )
-    })
-
-    const encodedString = floatNums.map(f => encodeNumber(f, { float: true }))
-    const decodedNumber = encodedString.map(s => decodeToNumber(s, { float: true }))
-
-    expect(floatNums.map(v => v.toFixed(FLOAT_PRECISION))).toEqual(decodedNumber.map(n => n.toFixed(FLOAT_PRECISION)))
-  })
-
-  it('poisoned hash should throw', () => {
-    const illegialCharacters = './\\?#!@#^%&*()+={}[]\'"\n\t;:'
-    for (const char of illegialCharacters.split('')) {
-      expect(() => {
-        decodeToNumber(char)
-      }).toThrow()
-    }
-  })
-
-  it('poisoned hash can be caught', () => {
-
-    const testArray = ['abc', './\\', 'Cde']
-    const decodedNum = testArray.map(v => {
-      try {
-        return decodeToNumber(v)
-      } catch (e) {
-        return null
-      }
-    }).filter(v => !!v)
-    expect(decodedNum.length).toEqual(2)
-  })
-})
diff --git a/src/atlasViewer/atlasViewer.constantService.service.ts b/src/atlasViewer/atlasViewer.constantService.service.ts
deleted file mode 100644
index b6e8358f72470566bf4350d61a78860038edf30c..0000000000000000000000000000000000000000
--- a/src/atlasViewer/atlasViewer.constantService.service.ts
+++ /dev/null
@@ -1,359 +0,0 @@
-import { HttpClient, HttpHeaders } from "@angular/common/http";
-import { Injectable, OnDestroy } from "@angular/core";
-import { select, Store } from "@ngrx/store";
-import { Observable, Subscription } from "rxjs";
-import { map, shareReplay } from "rxjs/operators";
-import { SNACKBAR_MESSAGE } from "src/services/state/uiState.store";
-import { IavRootStoreInterface } from "../services/stateStore.service";
-import { PureContantService } from "src/util";
-
-@Injectable({
-  providedIn : 'root',
-})
-
-export class AtlasViewerConstantsServices implements OnDestroy {
-
-  public darktheme: boolean = false
-  public darktheme$: Observable<boolean>
-
-  public citationToastDuration = 7e3
-
-  /**
-   * Timeout can be longer, since configs are lazy loaded.
-   */
-  private TIMEOUT = 16000
-
-  // instead of using window.location.href, which includes query param etc
-  public backendUrl = (BACKEND_URL && `${BACKEND_URL}/`.replace(/\/\/$/, '/')) || `${window.location.origin}${window.location.pathname}`
-
-  public totalTemplates = null
-
-  public getTemplateEndpoint$ = this.http.get(`${this.backendUrl}templates`, { responseType: 'json' }).pipe(
-    shareReplay(1)
-  )
-
-  public templateUrls = Array(100)
-
-  /* to be provided by KG in future */
-  private _mapArray: Array<[string, string[]]> = [
-    [ 'JuBrain Cytoarchitectonic Atlas' ,
-      [
-        'res/json/pmapsAggregatedData.json',
-        'res/json/receptorAggregatedData.json',
-      ],
-    ],
-    [
-      'Fibre Bundle Atlas - Short Bundle',
-      [
-        'res/json/swmAggregatedData.json',
-      ],
-    ],
-    [
-      'Allen Mouse Common Coordinate Framework v3 2015',
-      [
-        'res/json/allenAggregated.json',
-      ],
-    ],
-    [
-      'Fibre Bundle Atlas - Long Bundle',
-      [
-        'res/json/dwmAggregatedData.json',
-      ],
-    ],
-    [
-      'Whole Brain (v2.0)',
-      [
-        'res/json/waxholmAggregated.json',
-      ],
-    ],
-  ]
-
-  public mapParcellationNameToFetchUrl: Map<string, string[]> = new Map(this._mapArray)
-  public spatialSearchUrl = 'https://kg-int.humanbrainproject.org/solr/'
-  public spatialResultsPerPage = 10
-  public spatialWidth = 600
-
-  public chartBaseStyle = {
-    fill : 'origin',
-  }
-
-  public chartSdStyle = {
-    fill : false,
-    backgroundColor : 'rgba(0,0,0,0)',
-    borderDash : [10, 3],
-    pointRadius : 0,
-    pointHitRadius : 0,
-  }
-
-  public minReqMD = `
-# Hmm... it seems like we hit a snag
-It seems your browser has trouble loading interactive atlas viewer.
-Interactive atlas viewer requires **webgl2.0**, and the \`EXT_color_buffer_float\` extension enabled.
-- We recommend using _Chrome >= 56_ or _Firefox >= 51_. You can check your browsers' support of webgl2.0 by visiting <https://caniuse.com/#feat=webgl2>
-- If you are on _Chrome < 56_ or _Firefox < 51_, you may be able to enable **webgl2.0** by turning on experimental flag <https://get.webgl.org/webgl2/enable.html>.
-- If you are on an Android device we recommend _Chrome for Android_ or _Firefox for Android_.
-- Unfortunately, Safari and iOS devices currently do not support **webgl2.0**: <https://webkit.org/status/#specification-webgl-2>
-`
-  public minReqModalHeader = `Hmm... it seems your browser and is having trouble loading interactive atlas viewer`
-  public minReqWebGl2 = `Your browser does not support WebGL2.`
-  public minReqColorBufferFloat = `Your browser does not support EXT_color_bugger_float extension`
-
-  public mobileWarningHeader = `Power and Network Usage warning`
-  public mobileWarning = `It looks like you are on a mobile device. Please note that the atlas viewer is power and network usage intensive.`
-
-  /**
-   * When the selected regions becomes exceedingly many, referer header often gets too hard
-   * in nginx, it can result in 400 header to large
-   * as result, trim referer to only template and parcellation selected
-   */
-  private getScopedReferer(): string {
-    const url = new URL(window.location.href)
-    url.searchParams.delete('regionsSelected')
-    return url.toString()
-  }
-
-  public getHttpHeader(): HttpHeaders {
-    const header = new HttpHeaders()
-    header.set('referrer', this.getScopedReferer())
-    return header
-  }
-
-  public getFetchOption(): RequestInit {
-    return {
-      referrer: this.getScopedReferer(),
-    }
-  }
-
-  /**
-   * message when user on hover a segment or landmark
-   */
-  public toggleMessage: string = 'double click to toggle select, right click to search'
-
-  /**
-   * Observable for showing config modal
-   */
-  public showConfigTitle: string = 'Settings'
-
-  private showHelpGeneralMobile = [
-    ['hold 🌏 + ↕', 'change oblique slice mode'],
-    ['hold 🌏 + ↔', 'oblique slice'],
-  ]
-  private showHelpGeneralDesktop = [
-    ['num keys [0-9]', 'toggle layer visibility [0-9]'],
-    ['h', 'show help'],
-    ['?', 'show help'],
-    ['o', 'toggle perspective/orthographic'],
-  ]
-
-  public showHelpGeneralMap = this.showHelpGeneralDesktop
-
-  private showHelpSliceViewMobile = [
-    ['drag', 'pan'],
-  ]
-  private showHelpSliceViewDesktop = [
-    ['drag', 'pan'],
-    ['shift + drag', 'oblique slice'],
-  ]
-
-  public showHelpSliceViewMap = this.showHelpSliceViewDesktop
-
-  private showHelpPerspectiveMobile = [
-    ['drag', 'change perspective view'],
-  ]
-
-  private showHelpPerspectiveDesktop = [
-    ['drag', 'change perspective view'],
-  ]
-  public showHelpPerspectiveViewMap = this.showHelpPerspectiveDesktop
-
-  public repoUrl = `https://github.com/HumanBrainProject/interactive-viewer`
-  public supportEmailAddress = `support@ebrains.eu`
-  public docUrl = `https://interactive-viewer.readthedocs.io/en/latest/`
-
-  public showHelpSupportText: string = `Did you encounter an issue?
-Send us an email: <a target = "_blank" href = "mailto:${this.supportEmailAddress}">${this.supportEmailAddress}</a>
-
-Raise/track issues at github repo: <a target = "_blank" href = "${this.repoUrl}">${this.repoUrl}</a>
-`
-
-  public incorrectParcellationNameSearchParam(title) {
-    return `The selected parcellation - ${title} - is not available. The the first parcellation of the template is selected instead.`
-  }
-
-  public incorrectTemplateNameSearchParam(title) {
-    return `The selected template - ${title} - is not available.`
-  }
-
-  constructor(
-    private store$: Store<IavRootStoreInterface>,
-    private http: HttpClient,
-    private pureConstantService: PureContantService
-  ) {
-
-    this.darktheme$ = this.store$.pipe(
-      select('viewerState'),
-      select('templateSelected'),
-      map(template => {
-        if (!template) { return false }
-        return template.useTheme === 'dark'
-      }),
-      shareReplay(1),
-    )
-
-    this.subscriptions.push(
-      this.darktheme$.subscribe(flag => this.darktheme = flag),
-    )
-
-    this.subscriptions.push(
-      this.pureConstantService.useTouchUI$.subscribe(bool => {
-        if (bool) {
-          this.showHelpSliceViewMap = this.showHelpSliceViewMobile
-          this.showHelpGeneralMap = this.showHelpGeneralMobile
-          this.showHelpPerspectiveViewMap = this.showHelpPerspectiveMobile
-          this.dissmissUserLayerSnackbarMessage = this.dissmissUserLayerSnackbarMessageMobile
-        } else {
-          this.showHelpSliceViewMap = this.showHelpSliceViewDesktop
-          this.showHelpGeneralMap = this.showHelpGeneralDesktop
-          this.showHelpPerspectiveViewMap = this.showHelpPerspectiveDesktop
-          this.dissmissUserLayerSnackbarMessage = this.dissmissUserLayerSnackbarMessageDesktop
-        }
-      }),
-    ),
-    this.pureConstantService.getTemplateEndpoint$.subscribe(arr => {
-      this.totalTemplates = arr.length
-    })
-  }
-
-  private subscriptions: Subscription[] = []
-
-  public ngOnDestroy() {
-    while (this.subscriptions.length > 0) {
-      this.subscriptions.pop().unsubscribe()
-    }
-  }
-
-  public catchError(e: Error | string) {
-    this.store$.dispatch({
-      type: SNACKBAR_MESSAGE,
-      snackbarMessage: e.toString(),
-    })
-  }
-
-  private dissmissUserLayerSnackbarMessageDesktop = `You can dismiss extra layers with [ESC]`
-  private dissmissUserLayerSnackbarMessageMobile = `You can dismiss extra layers in the 🌏 menu`
-  public dissmissUserLayerSnackbarMessage: string = this.dissmissUserLayerSnackbarMessageDesktop
-}
-
-export const UNSUPPORTED_PREVIEW = [{
-  text: 'Preview of Colin 27 and JuBrain Cytoarchitectonic',
-  previewSrc: './res/image/1.png',
-}, {
-  text: 'Preview of Big Brain 2015 Release',
-  previewSrc: './res/image/2.png',
-}, {
-  text: 'Preview of Waxholm Rat V2.0',
-  previewSrc: './res/image/3.png',
-}]
-
-export const UNSUPPORTED_INTERVAL = 7000
-
-
-/**
- * First attempt at encoding int (e.g. selected region, navigation location) from number (loc info density) to b64 (higher info density)
- * The constraint is that the cipher needs to be commpatible with URI encoding
- * and a URI compatible separator is required.
- *
- * The implementation below came from
- * https://stackoverflow.com/a/6573119/6059235
- *
- * While a faster solution exist in the same post, this operation is expected to be done:
- * - once per 1 sec frequency
- * - on < 1000 numbers
- *
- * So performance is not really that important (Also, need to learn bitwise operation)
- */
-
-const cipher = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-'
-export const separator = "."
-const negString = '~'
-
-const encodeInt = (number: number) => {
-  if (number % 1 !== 0) { throw new Error('cannot encodeInt on a float. Ensure float flag is set') }
-  if (isNaN(Number(number)) || number === null || number === Number.POSITIVE_INFINITY) { throw new Error('The input is not valid') }
-
-  let rixit // like 'digit', only in some non-decimal radix
-  let residual
-  let result = ''
-
-  if (number < 0) {
-    result += negString
-    residual = Math.floor(number * -1)
-  } else {
-    residual = Math.floor(number)
-  }
-
-  /* eslint-disable-next-line no-constant-condition */
-  while (true) {
-    rixit = residual % 64
-    // this.log.log("rixit : " + rixit)
-    // this.log.log("result before : " + result)
-    result = cipher.charAt(rixit) + result
-    // this.log.log("result after : " + result)
-    // this.log.log("residual before : " + residual)
-    residual = Math.floor(residual / 64)
-    // this.log.log("residual after : " + residual)
-
-    if (residual === 0) {
-      break;
-    }
-  }
-  return result
-}
-
-interface IB64EncodingOption {
-  float: boolean
-}
-
-const defaultB64EncodingOption = {
-  float: false,
-}
-
-export const encodeNumber:
-  (number: number, option?: IB64EncodingOption) => string =
-  (number: number, { float = false }: IB64EncodingOption = defaultB64EncodingOption) => {
-    if (!float) { return encodeInt(number) } else {
-      const floatArray = new Float32Array(1)
-      floatArray[0] = number
-      const intArray = new Uint32Array(floatArray.buffer)
-      const castedInt = intArray[0]
-      return encodeInt(castedInt)
-    }
-  }
-
-const decodetoInt = (encodedString: string) => {
-  let _encodedString
-  let negFlag = false
-  if (encodedString.slice(-1) === negString) {
-    negFlag = true
-    _encodedString = encodedString.slice(0, -1)
-  } else {
-    _encodedString = encodedString
-  }
-  return (negFlag ? -1 : 1) * [..._encodedString].reduce((acc, curr) => {
-    const index = cipher.indexOf(curr)
-    if (index < 0) { throw new Error(`Poisoned b64 encoding ${encodedString}`) }
-    return acc * 64 + index
-  }, 0)
-}
-
-export const decodeToNumber:
-  (encodedString: string, option?: IB64EncodingOption) => number =
-  (encodedString: string, {float = false} = defaultB64EncodingOption) => {
-    if (!float) { return decodetoInt(encodedString) } else {
-      const _int = decodetoInt(encodedString)
-      const intArray = new Uint32Array(1)
-      intArray[0] = _int
-      const castedFloat = new Float32Array(intArray.buffer)
-      return castedFloat[0]
-    }
-  }
diff --git a/src/atlasViewer/atlasViewer.history.service.spec.ts b/src/atlasViewer/atlasViewer.history.service.spec.ts
deleted file mode 100644
index 97e1e6fd194a3e4a301d8716fc4f2d614b13f47d..0000000000000000000000000000000000000000
--- a/src/atlasViewer/atlasViewer.history.service.spec.ts
+++ /dev/null
@@ -1,125 +0,0 @@
-import { AtlasViewerHistoryUseEffect } from './atlasViewer.history.service'
-import { TestBed, tick, fakeAsync, flush } from '@angular/core/testing'
-import { provideMockActions } from '@ngrx/effects/testing'
-import { provideMockStore } from '@ngrx/store/testing'
-import { Observable, of, Subscription } from 'rxjs'
-import { Action, Store } from '@ngrx/store'
-import { defaultRootState } from '../services/stateStore.service'
-import { cold } from 'jasmine-marbles'
-import { HttpClientTestingModule } from '@angular/common/http/testing'
-
-const bigbrainJson = require('!json-loader!src/res/ext/bigbrain.json')
-
-const actions$: Observable<Action> = of({type: 'TEST'})
-
-describe('atlasviewer.history.service.ts', () => {
-  beforeEach(() => {
-    TestBed.configureTestingModule({
-      imports: [
-        HttpClientTestingModule
-      ],
-      providers: [
-        AtlasViewerHistoryUseEffect,
-        provideMockActions(() => actions$),
-        provideMockStore({ initialState: defaultRootState })
-      ]
-    })
-  })
-
-  afterEach(() => {
-  })
-
-  describe('currentStateSearchParam$', () => {
-
-    it('should fire when template is set', () => {
-
-      const effect = TestBed.get(AtlasViewerHistoryUseEffect)
-      const store = TestBed.get(Store)
-      const { viewerState } = defaultRootState
-      store.setState({
-        ...defaultRootState,
-        viewerState: {
-          ...viewerState,
-          templateSelected: bigbrainJson
-        }
-      })
-      
-      const expected = cold('(a)', {
-        a: 'templateSelected=Big+Brain+%28Histology%29'
-      })
-      expect(effect.currentStateSearchParam$).toBeObservable(expected)
-    })
-  
-    it('should fire when template and parcellation is set', () => {
-  
-      const effect = TestBed.get(AtlasViewerHistoryUseEffect)
-      const store = TestBed.get(Store)
-      const { viewerState } = defaultRootState
-      store.setState({
-        ...defaultRootState,
-        viewerState: {
-          ...viewerState,
-          templateSelected: bigbrainJson,
-          parcellationSelected: bigbrainJson.parcellations[0]
-        }
-      })
-      
-      const expected = cold('(a)', {
-        a: 'templateSelected=Big+Brain+%28Histology%29&parcellationSelected=Cytoarchitectonic+Maps+-+v2.4'
-      })
-      
-      expect(effect.currentStateSearchParam$).toBeObservable(expected)
-    })
-  })
-
-
-  describe('setNewSearchString$', () => {
-
-    const obj = {
-      spiedFn: () => {}
-    }
-    const subscriptions: Subscription[] = []
-
-    let spy
-
-    beforeAll(() => {
-      spy = spyOn(obj, 'spiedFn')
-    })
-
-    beforeEach(() => {
-      spy.calls.reset()
-    })
-
-    afterEach(() => {
-      while (subscriptions.length > 0) subscriptions.pop().unsubscribe()
-    })
-
-    it('should fire when set', fakeAsync(() => {
-
-      const store = TestBed.get(Store)
-      const effect = TestBed.get(AtlasViewerHistoryUseEffect)
-      subscriptions.push(
-        effect.setNewSearchString$.subscribe(obj.spiedFn)
-      )
-      const { viewerState } = defaultRootState
-  
-      store.setState({
-        ...defaultRootState,
-        viewerState: {
-          ...viewerState,
-          templateSelected: bigbrainJson,
-          parcellationSelected: bigbrainJson.parcellations[0]
-        }
-      })
-      tick(100)
-      expect(spy).toHaveBeenCalledTimes(1)
-    }))
-
-    it('should not call window.history.pushState on start', fakeAsync(() => {
-      tick(100)
-      expect(spy).toHaveBeenCalledTimes(0)
-    }))
-  
-  })
-  
-})
\ No newline at end of file
diff --git a/src/atlasViewer/atlasViewer.history.service.ts b/src/atlasViewer/atlasViewer.history.service.ts
deleted file mode 100644
index 6df36758a8d45a192a1d8f2ea79e7ab728075285..0000000000000000000000000000000000000000
--- a/src/atlasViewer/atlasViewer.history.service.ts
+++ /dev/null
@@ -1,147 +0,0 @@
-import { Injectable, OnDestroy } from "@angular/core";
-import { Actions, Effect, ofType } from '@ngrx/effects'
-import { Store } from "@ngrx/store";
-import { fromEvent, merge, of, Subscription } from "rxjs";
-import { debounceTime, distinctUntilChanged, filter, map, startWith, switchMap, switchMapTo, take, withLatestFrom, shareReplay } from "rxjs/operators";
-import { defaultRootState, GENERAL_ACTION_TYPES, IavRootStoreInterface } from "src/services/stateStore.service";
-import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service";
-import { cvtSearchParamToState, cvtStateToSearchParam } from "./atlasViewer.urlUtil";
-import { viewerStateHelperStoreName } from '../services/state/viewerState.store.helper'
-import { PureContantService } from "src/util";
-const getSearchParamStringFromState = state => {
-  try {
-    return cvtStateToSearchParam(state).toString()
-  } catch (e) {
-    throw new Error(`cvt state to search param error ${e.toString()}`)
-  }
-}
-
-@Injectable({
-  providedIn: 'root',
-})
-
-export class AtlasViewerHistoryUseEffect implements OnDestroy {
-
-  // ensure that fetchedTemplates are all populated
-  @Effect()
-  public parsingSearchUrlToState$ = this.store$.pipe(
-    filter(state => {
-      return state.viewerState.fetchedTemplates.length === this.constantService.totalTemplates &&
-        state[viewerStateHelperStoreName].fetchedAtlases.length === this.pureConstantSErvice.totalAtlasesLength
-    }),
-    take(1),
-    switchMapTo(merge(
-      // parsing state can occur via 2 ways:
-      // either pop state event or on launch
-      fromEvent(window, 'popstate').pipe(
-        map(({ state }: PopStateEvent) => state),
-      ),
-      of(new URLSearchParams(window.location.search).toString()),
-    )),
-  ).pipe(
-    withLatestFrom(this.store$),
-    map(([searchUrl, storeState]: [string, any] ) => {
-      const search = new URLSearchParams(searchUrl)
-      try {
-        if (Array.from(search.keys()).length === 0) {
-          // if empty searchParam
-          return {
-            type: GENERAL_ACTION_TYPES.APPLY_STATE,
-            state: {
-              ...defaultRootState,
-              ...storeState,
-              viewerState: {
-                ...defaultRootState.viewerState,
-                fetchedTemplates: storeState.viewerState.fetchedTemplates,
-              },
-            },
-          }
-        } else {
-          // if non empty search param
-          const newState = cvtSearchParamToState(search, storeState)
-          return {
-            type: GENERAL_ACTION_TYPES.APPLY_STATE,
-            state: newState,
-          }
-        }
-      } catch (e) {
-        // usually parsing error
-        // TODO should log
-        return {
-          type: GENERAL_ACTION_TYPES.APPLY_STATE,
-          state: {
-            ...defaultRootState,
-            viewerState: {
-              ...defaultRootState.viewerState,
-              fetchedTemplates: storeState.viewerState.fetchedTemplates,
-            },
-            viewerStateHelper: {
-              ...defaultRootState.viewerStateHelper,
-              fetchedAtlases: storeState.viewerStateHelper.fetchedAtlases
-            }
-          },
-        }
-      }
-    }),
-  )
-
-  private subscriptions: Subscription[] = []
-
-  private currentStateSearchParam$ = this.store$.pipe(
-    map(s => {
-      try {
-        return getSearchParamStringFromState(s)
-      } catch (e) {
-        // TODO parsing state to search param error
-        return null
-      }
-    }),
-    filter(v => v !==  null),
-  )
-
-  // GENERAL_ACTION_TYPES.APPLY_STATE is triggered by pop state or initial
-  // conventiently, the action has a state property
-  public setNewSearchString$ = this.actions$.pipe(
-    ofType(GENERAL_ACTION_TYPES.APPLY_STATE),
-    // subscribe to inner obs on init
-    startWith({}),
-    switchMap(({ state }: any) =>
-      this.currentStateSearchParam$.pipe(
-        shareReplay(1),
-        distinctUntilChanged(),
-        debounceTime(100),
-
-        // compares the searchParam triggerd by change of state with the searchParam generated by GENERAL_ACTION_TYPES.APPLY_STATE
-        // if the same, the change is induced by GENERAL_ACTION_TYPES.APPLY_STATE, and should NOT be pushed to history
-        filter((newSearchParam, index) => {
-          try {
-            const oldSearchParam = (state && getSearchParamStringFromState(state)) || ''
-
-            // in the unlikely event that user returns to the exact same state without use forward/back button
-            return index > 0 || newSearchParam !== oldSearchParam
-          } catch (e) {
-            return index > 0 || newSearchParam !== ''
-          }
-        })
-      )
-    )
-  )
-
-  constructor(
-    private store$: Store<IavRootStoreInterface>,
-    private actions$: Actions,
-    private constantService: AtlasViewerConstantsServices,
-    private pureConstantSErvice: PureContantService
-  ) {
-
-    this.setNewSearchString$.subscribe(newSearchString => {
-      const url = new URL(window.location.toString())
-      url.search = newSearchString
-      window.history.pushState(newSearchString, '', url.toString())
-    })
-  }
-
-  public ngOnDestroy() {
-    while (this.subscriptions.length > 0) { this.subscriptions.pop().unsubscribe() }
-  }
-}
diff --git a/src/atlasViewer/atlasViewer.style.css b/src/atlasViewer/atlasViewer.style.css
index c5024d44227a8cb0e0594637e583f392418efcbb..d89f76ce6b5edcc4463c44fd0c80db2465b002a8 100644
--- a/src/atlasViewer/atlasViewer.style.css
+++ b/src/atlasViewer/atlasViewer.style.css
@@ -8,6 +8,7 @@
 :host[darktheme="true"]
 {
   background-color:black;
+  display: block;
 }
 
 ui-nehuba-container
@@ -68,7 +69,6 @@ mat-sidenav {
   box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
 }
 
-landmark-ui,
 region-menu
 {
   display:inline-block;
diff --git a/src/atlasViewer/atlasViewer.template.html b/src/atlasViewer/atlasViewer.template.html
index a45ee29bc896c9957348da909d03410244d579b8..01d2391c3916c8e6eeb1706c711aab6ee4df3825 100644
--- a/src/atlasViewer/atlasViewer.template.html
+++ b/src/atlasViewer/atlasViewer.template.html
@@ -40,312 +40,24 @@
 
 <!-- atlas template -->
 <ng-template #viewerBody>
-  <div class="atlas-container"
+  <div class="atlas-container w-100 h-100"
     iav-media-query
+    quick-tour
+    [quick-tour-position]="quickTourFinale.position"
+    [quick-tour-description]="quickTourFinale.description"
+    [quick-tour-description-md]="quickTourFinale.descriptionMd"
+    [quick-tour-order]="quickTourFinale.order"
+    [quick-tour-overwrite-arrow]="emptyArrowTmpl"
+    quick-tour-severity="low"
     #media="iavMediaQuery">
     <!-- prevent default is required so that user do not zoom in on UI or scroll on mobile UI -->
-    <ui-nehuba-container
-      class="z-index-10"
-      #uiNehubaContainer="uiNehubaContainer"
-      iav-mouse-hover
-      #iavMouseHoverEl="iavMouseHover"
-      [currentOnHoverObs$]="iavMouseHoverEl.currentOnHoverObs$"
-      [currentOnHover]="iavMouseHoverEl.currentOnHoverObs$ | async"
+    <iav-cmp-viewer-container
+      class="w-100 h-100 d-block"
+      [ismobile]="(media.mediaBreakPoint$ | async) > 3"
       iav-captureClickListenerDirective
       [iav-captureClickListenerDirective-captureDocument]="true"
-      (iav-captureClickListenerDirective-onUnmovedClick)="mouseClickDocument($event)"
-      (drag-drop)="localFileService.handleFileDrop($event)">
-
-      <!-- top right content transclusion -->
-      <div ui-nehuba-container-overlay-top-right class="d-inline-flex flex-row justify-content-end align-items-start z-index-6 position-absolute pe-none w-100 h-100">
-
-        <signin-banner
-          class="mt-3 mr-2"
-          [parcellationIsSelected]="!!selectedParcellation"
-          [ismobile]="(media.mediaBreakPoint$ | async) > 3">
-        </signin-banner>
-
-        <!-- atlas selector -->
-        <div *ngIf="uiNehubaContainer.viewerLoaded"
-          class="iv-custom-comp bg card m-2 mat-elevation-z2">
-          <atlas-dropdown-selector class="pe-all mt-2">
-          </atlas-dropdown-selector>
-        </div>
-
-      </div>
-
-      <!-- bottom left content transclusion -->
-      <div ui-nehuba-container-overlay-bottom-left class="d-inline-flex pe-none w-100 align-items-end m-2 mb-4">
-
-        <!-- only load atlas layer selector and chips if viewer is loaded -->
-        <ng-template [ngIf]="uiNehubaContainer.viewerLoaded  && !(isStandaloneVolumes$ | async)">
-
-          <!-- Viewer Selector Container-->
-          <atlas-layer-selector
-            #alSelector="atlasLayerSelector"
-            class="pe-all"
-            (iav-outsideClick)="alSelector.selectorExpanded = false">
-          </atlas-layer-selector>
-          <mat-chip-list class="mb-2">
-            <!-- additional layer -->
-
-            <ng-container>
-              <ng-container *ngTemplateOutlet="currParcellationTmpl; context: { addParc: (selectedAdditionalLayers$ | async), parc: selectedParcellation }">
-              </ng-container>
-            </ng-container>
-
-            <!-- any selected region(s) -->
-            <ng-container>
-              <ng-container *ngTemplateOutlet="selectedRegionTmpl">
-              </ng-container>
-            </ng-container>
-
-            <!-- controls for iav volumes -->
-            <div class="hidden" iav-shown-previews #previews="iavShownPreviews"></div>
-            <ng-container *ngTemplateOutlet="selectedDatasetPreview; context: { layers: previews.iavAdditionalLayers$ | async | filterPreviewByType : [previews.FILETYPES.VOLUMES] }">
-            </ng-container>
-
-          </mat-chip-list>
-
-          <!-- current layer tmpl -->
-
-          <ng-template #currParcellationTmpl let-parc="parc" let-addParc="addParc">
-
-            <div [matMenuTriggerFor]="layerVersionMenu"
-              [matMenuTriggerData]="{ layerVersionMenuTrigger: layerVersionMenuTrigger }"
-              #layerVersionMenuTrigger="matMenuTrigger">
-
-              <ng-template [ngIf]="addParc.length > 0" [ngIfElse]="defaultParcTmpl">
-                <ng-container *ngFor="let p of addParc">
-                  <ng-container *ngTemplateOutlet="chipTmpl; context: {
-                    parcel: p,
-                    selected: true,
-                    dismissable: true,
-                    onclick: layerVersionMenuTrigger.toggleMenu.bind(layerVersionMenuTrigger)
-                  }">
-                  </ng-container>
-                </ng-container>
-              </ng-template>
-              <ng-template #defaultParcTmpl>
-                <ng-container *ngTemplateOutlet="chipTmpl; context: {
-                  parcel: parc,
-                  selected: false,
-                  dismissable: false,
-                  onclick: layerVersionMenuTrigger.toggleMenu.bind(layerVersionMenuTrigger)
-                }">
-                </ng-container>
-              </ng-template>
-            </div>
-          </ng-template>
-
-          <!-- render parc templ -->
-          <ng-template #chipTmpl
-            let-parcel="parcel"
-            let-selected="selected"
-            let-dismissable="dismissable"
-            let-chipClass="class"
-            let-onclick="onclick">
-            <mat-chip class="pe-all position-relative z-index-2 d-inline-flex justify-content-between"
-              [ngClass]="chipClass"
-              (click)="onclick && onclick()"
-              [selected]="selected">
-
-              <span>
-                {{ parcel?.groupName ? (parcel?.groupName + ' - ') : '' }}{{ parcel && (parcel.displayName || parcel.name) }}
-              </span>
-
-              <!-- info icon -->
-              <ng-template [ngIf]="parcel?.originDatasets?.length > 0" [ngIfElse]="infoIconBasic">
-
-                <mat-icon
-                  *ngFor="let ds of parcel.originDatasets"
-                  fontSet="fas"
-                  fontIcon="fa-info-circle"
-                  iav-stop="click"
-                  iav-dataset-show-dataset-dialog
-                  [iav-dataset-show-dataset-dialog-kgid]="ds['kgId']"
-                  [iav-dataset-show-dataset-dialog-kgschema]="ds['kgSchema']"
-                  [iav-dataset-show-dataset-dialog-name]="parcel?.properties?.name"
-                  [iav-dataset-show-dataset-dialog-description]="parcel?.properties?.description">
-                </mat-icon>
-
-              </ng-template>
-
-              <ng-template #infoIconBasic>
-                <mat-icon *ngIf="parcel?.properties?.name && parcel?.properties?.description"
-                  fontSet="fas"
-                  fontIcon="fa-info-circle"
-                  iav-stop="click"
-                  iav-dataset-show-dataset-dialog
-                  [iav-dataset-show-dataset-dialog-name]="parcel.properties.name"
-                  [iav-dataset-show-dataset-dialog-description]="parcel.properties.description">
-
-                </mat-icon>
-              </ng-template>
-
-              <!-- dismiss icon -->
-              <mat-icon
-                *ngIf="dismissable"
-                (click)="clearAdditionalLayer(parcel); $event.stopPropagation()"
-                fontSet="fas"
-                fontIcon="fa-times">
-              </mat-icon>
-            </mat-chip>
-          </ng-template>
-
-          <!-- layer version selector -->
-          <mat-menu #layerVersionMenu
-            class="bg-none box-shadow-none"
-            [hasBackdrop]="false">
-            <ng-template matMenuContent let-layerVersionMenuTrigger="layerVersionMenuTrigger">
-              <div (iav-outsideClick)="layerVersionMenuTrigger.closeMenu()">
-                <ng-container *ngFor="let parcVer of selectedLayerVersions$ | async">
-                  <ng-container *ngTemplateOutlet="chipTmpl; context: {
-                    parcel: parcVer,
-                    selected: selectedParcellation && selectedParcellation['@id'] === parcVer['@id'],
-                    dismissable: false,
-                    class: 'w-100',
-                    onclick: bindFns([
-                      [ selectParcellation.bind(this), parcVer ],
-                      [ layerVersionMenuTrigger.closeMenu.bind(layerVersionMenuTrigger) ]
-                    ])
-                  }">
-                  </ng-container>
-                  <div class="mt-1"></div>
-                </ng-container>
-              </div>
-            </ng-template>
-          </mat-menu>
-
-          <ng-template #selectedRegionTmpl>
-
-            <!-- regions chip -->
-            <ng-template [ngIf]="selectedRegions$ | async" let-selectedRegions="ngIf">
-              <!-- if regions.length > 1 -->
-              <!-- use group chip -->
-              <ng-template [ngIf]="selectedRegions.length > 1" [ngIfElse]="singleRegionTmpl">
-                <mat-chip
-                  color="primary"
-                  selected
-                  (click)="uiNehubaContainer.matDrawerMinor.open() && uiNehubaContainer.navSideDrawerMainSwitch.open()"
-                  class="pe-all position-relative z-index-1 ml-8-n">
-                  <span class="iv-custom-comp text text-truncate d-inline pl-4">
-                    {{ CONST.MULTI_REGION_SELECTION }}
-                  </span>
-                  <mat-icon
-                    (click)="clearSelectedRegions()"
-                    fontSet="fas"
-                    iav-stop="click"
-                    fontIcon="fa-times">
-                  </mat-icon>
-                </mat-chip>
-              </ng-template>
-
-              <!-- if reginos.lengt === 1 -->
-              <!-- use single region chip -->
-              <ng-template #singleRegionTmpl>
-                <ng-container *ngFor="let r of selectedRegions">
-
-                  <!-- region chip for discrete map -->
-                  <mat-chip
-                    iav-region
-                    (click)="uiNehubaContainer.matDrawerMinor.open() && uiNehubaContainer.navSideDrawerMainSwitch.open()"
-                    [region]="r"
-                    class="pe-all position-relative z-index-1 ml-8-n"
-                    [ngClass]="{
-                      'darktheme':regionDirective.rgbDarkmode === true,
-                      'lighttheme': regionDirective.rgbDarkmode === false
-                    }"
-                    [style.backgroundColor]="regionDirective.rgbString"
-                    #regionDirective="iavRegion">
-                    <span class="iv-custom-comp text text-truncate d-inline pl-4">
-                      {{ r.name }}
-                    </span>
-                    <mat-icon
-                      class="iv-custom-comp text"
-                      (click)="clearSelectedRegions()"
-                      fontSet="fas"
-                      iav-stop="click"
-                      fontIcon="fa-times">
-                    </mat-icon>
-                  </mat-chip>
-    
-                  <!-- chips for previewing origin datasets/continous map -->
-                  <ng-container *ngFor="let originDataset of (r.originDatasets || []); let index = index">
-                    <div class="hidden"
-                      iav-dataset-preview-dataset-file
-                      [iav-dataset-preview-dataset-file-kgid]="originDataset.kgId"
-                      [iav-dataset-preview-dataset-file-filename]="originDataset.filename"
-                      #previewDirective="iavDatasetPreviewDatasetFile">
-                    </div>
-                    <mat-chip *ngIf="previewDirective.active"
-                      (click)="uiNehubaContainer.matDrawerMinor.open() && uiNehubaContainer.navSideDrawerMainSwitch.open()"
-                      class="pe-all position-relative ml-8-n">
-                      <span class="pl-4">
-                        {{ regionDirective.regionOriginDatasetLabels$ | async | renderViewOriginDatasetlabel : index }}
-                      </span>
-                      <mat-icon (click)="previewDirective.onClick()"
-                        fontSet="fas"
-                        iav-stop="click"
-                        fontIcon="fa-times">
-                      </mat-icon>
-                    </mat-chip>
-    
-                    <mat-chip *ngFor="let key of clearViewKeys$ | async"
-                      (click)="uiNehubaContainer.matDrawerMinor.open() && uiNehubaContainer.navSideDrawerMainSwitch.open()"
-                      class="pe-all position-relative ml-8-n">
-                      <span class="pl-4">
-                        {{ key }}
-                      </span>
-                      <mat-icon (click)="unsetClearViewByKey(key)"
-                        fontSet="fas"
-                        iav-stop="click"
-                        fontIcon="fa-times">
-    
-                      </mat-icon>
-                    </mat-chip>
-                  </ng-container>
-    
-                </ng-container>
-              </ng-template>
-            </ng-template>
-
-          </ng-template>
-
-          <ng-template #selectedDatasetPreview let-layers="layers">
-
-            <ng-container *ngFor="let layer of layers">
-              <div class="hidden"
-                iav-dataset-preview-dataset-file
-                [iav-dataset-preview-dataset-file-kgid]="layer.datasetId"
-                [iav-dataset-preview-dataset-file-filename]="layer.filename"
-                #preview="iavDatasetPreviewDatasetFile">
-
-              </div>
-              <mat-chip class="pe-all"
-                (click)="uiNehubaContainer.matDrawerMinor.open() && uiNehubaContainer.navSideDrawerMainSwitch.open()">
-                {{ layer.file?.name || layer.filename || 'Unknown data preview' }}
-                <mat-icon fontSet="fas" fontIcon="fa-times"
-                  (click)="preview.onClick()"
-                  iav-stop="click">
-                </mat-icon>
-              </mat-chip>
-            </ng-container>
-          </ng-template>
-
-        </ng-template>
-      </div>
-
-      <!-- top left content transclusion -->
-      <div ui-nehuba-container-overlay-top-left class="d-inline-flex pe-none w-100 align-items-start m-2">
-        <ui-status-card
-          *ngIf="uiNehubaContainer.viewerLoaded"
-          class="pe-all muted-7"
-          [selectedTemplateName]="uiNehubaContainer?.selectedTemplate?.name"
-          [nehubaViewer]="uiNehubaContainer?.nehubaViewer">
-        </ui-status-card>
-      </div>
-    </ui-nehuba-container>
+      (iav-captureClickListenerDirective-onUnmovedClick)="mouseClickDocument($event)">
+    </iav-cmp-viewer-container>
 
     <layout-floating-container
       zIndex="13"
@@ -453,3 +165,6 @@
 <ng-template #idleOverlay>
   <tryme-component></tryme-component>
 </ng-template>
+
+<ng-template #emptyArrowTmpl>
+</ng-template>
diff --git a/src/atlasViewer/atlasViewer.urlUtil.spec.ts b/src/atlasViewer/atlasViewer.urlUtil.spec.ts
deleted file mode 100644
index c018f8718488fad6cc61f7508bda4cca3bae8332..0000000000000000000000000000000000000000
--- a/src/atlasViewer/atlasViewer.urlUtil.spec.ts
+++ /dev/null
@@ -1,210 +0,0 @@
-// tslint:disable:no-empty
-
-import {} from 'jasmine'
-import { defaultRootState } from 'src/services/stateStore.service'
-import { cvtSearchParamToState, cvtStateToSearchParam } from './atlasViewer.urlUtil'
-
-const bigbrainJson = require('!json-loader!src/res/ext/bigbrain.json')
-const colin = require('!json-loader!src/res/ext/colin.json')
-const mni152 = require('!json-loader!src/res/ext/MNI152.json')
-const mni152Nehubaconfig = require('!json-loader!src/res/ext/MNI152NehubaConfig.json')
-const allen = require('!json-loader!src/res/ext/allenMouse.json')
-const waxholm = require('!json-loader!src/res/ext/waxholmRatV2_0.json')
-const atlasHumanMultilevel = require('!json-loader!src/res/ext/atlas/atlas_multiLevelHuman.json')
-
-const { defaultState: viewerHelperDefaultState,viewerStateHelperStoreName } = require('src/services/state/viewerState.store.helper')
-
-const { viewerState, ...rest } = defaultRootState
-const fetchedTemplateRootState = {
-  ...rest,
-  viewerState: {
-    ...viewerState,
-    fetchedTemplates: [ bigbrainJson, colin, mni152, allen, waxholm ],
-  },
-  [viewerStateHelperStoreName]: {
-    ...viewerHelperDefaultState,
-    fetchedAtlases: [ atlasHumanMultilevel ]
-  }
-}
-
-// TODO finish writing tests
-describe('atlasViewer.urlUtil.ts', () => {
-  describe('cvtSearchParamToState', () => {
-
-    it('> convert empty search param to empty state', () => {
-      const searchparam = new URLSearchParams()
-      expect(() => cvtSearchParamToState(searchparam, defaultRootState)).toThrow()
-    })
-
-    it('> parses template into atlasId properly', () => {
-      const searchparam = new URLSearchParams()
-      searchparam.set('templateSelected', bigbrainJson.name)
-      
-      const newState = cvtSearchParamToState(searchparam, fetchedTemplateRootState)
-      expect(
-        newState[viewerStateHelperStoreName]['selectedAtlasId']
-      ).toEqual(
-        atlasHumanMultilevel['@id']
-      )
-    })
-
-    describe('> parses parcellation selected into overlayingAdditionalParcellations properly', () => {
-      it('> if the selected layer is base layer, it should not populate overlayingAdditionalParcellations', () => {
-        const searchparam = new URLSearchParams()
-
-        searchparam.set('templateSelected', bigbrainJson.name)
-        searchparam.set('parcellationSelected', bigbrainJson.parcellations[0].name)
-
-        const newState = cvtSearchParamToState(searchparam, fetchedTemplateRootState)
-        expect(
-          newState[viewerStateHelperStoreName]['overlayingAdditionalParcellations']
-        ).toEqual([])
-      })
-
-      it('> if the selected layer is non base layer, it should be populated', () => {
-        const searchparam = new URLSearchParams()
-
-        searchparam.set('templateSelected', bigbrainJson.name)
-        searchparam.set('parcellationSelected', bigbrainJson.parcellations[1].name)
-
-        const newState = cvtSearchParamToState(searchparam, fetchedTemplateRootState)
-        expect(
-          newState[viewerStateHelperStoreName]['overlayingAdditionalParcellations'].length
-        ).toEqual(1)
-        expect(
-          newState[viewerStateHelperStoreName]['overlayingAdditionalParcellations'][0]['@id']
-        ).toEqual(bigbrainJson.parcellations[1]['@id'])
-      })
-    })
-
-    it('> successfully converts with only template defined', () => {
-      const searchparam = new URLSearchParams('?templateSelected=Big+Brain+%28Histology%29')
-
-      const newState = cvtSearchParamToState(searchparam, fetchedTemplateRootState)
-
-      const { parcellationSelected, templateSelected } = newState.viewerState
-      expect(templateSelected.name).toEqual(bigbrainJson.name)
-      expect(parcellationSelected.name).toEqual(bigbrainJson.parcellations[0].name)
-    })
-
-    it('successfully converts with template AND parcellation defined', () => {
-      const searchparam = new URLSearchParams()
-      searchparam.set('templateSelected', mni152.name)
-      searchparam.set('parcellationSelected', mni152.parcellations[1].name)
-
-      const newState = cvtSearchParamToState(searchparam, fetchedTemplateRootState)
-
-      const { parcellationSelected, templateSelected } = newState.viewerState
-      expect(templateSelected.name).toEqual(mni152.name)
-      expect(parcellationSelected.name).toEqual(mni152.parcellations[1].name)
-    })
-
-    it('successfully converts with template, parcellation AND selected regions defined', () => {
-
-    })
-
-    it('parses cNavigation correctly', () => {
-
-    })
-
-    describe('niftiLayers', () => {
-      let searchparam = new URLSearchParams()
-      beforeEach(() => {
-        searchparam = new URLSearchParams()
-        searchparam.set('templateSelected', mni152.name)
-        searchparam.set('parcellationSelected', mni152.parcellations[1].name)
-      })
-
-
-      it('parses niftiLayers correctly', () => {
-        const uri1 = `https://neuroglancer.humanbrainproject.eu/precomputed/JuBrain/17/icbm152casym/pmaps/Visual_hOc1_r_N10_nlin2MNI152ASYM2009C_2.4_publicP_a48ca5d938781ebaf1eaa25f59df74d0.nii.gz`
-        const uri2 = `https://neuroglancer.humanbrainproject.eu/precomputed/JuBrain/17/icbm152casym/pmaps/Visual_hOc1_r_N10_nlin2MNI152ASYM2009C_2000.4_publicP_a48ca5d938781ebaf1eaa25f59df74d0.nii.gz`
-        searchparam.set('niftiLayers', [uri1, uri2].join('__'))
-  
-        const newState = cvtSearchParamToState(searchparam, fetchedTemplateRootState)
-        expect(newState.ngViewerState.layers.length).toEqual(2)
-  
-        const layer1 = newState.ngViewerState.layers[0]
-        expect(layer1.name).toEqual(uri1)
-        expect(layer1.source).toEqual(`nifti://${uri1}`)
-        expect(layer1.mixability).toEqual('nonmixable')
-
-        const layer2 = newState.ngViewerState.layers[1]
-        expect(layer2.name).toEqual(uri2)
-        expect(layer2.source).toEqual(`nifti://${uri2}`)
-        expect(layer2.mixability).toEqual('nonmixable')
-      })
-      
-      it('parses multiple niftiLayers correctly', () => {
-
-        const uri = `https://neuroglancer.humanbrainproject.eu/precomputed/JuBrain/17/icbm152casym/pmaps/Visual_hOc1_r_N10_nlin2MNI152ASYM2009C_2.4_publicP_a48ca5d938781ebaf1eaa25f59df74d0.nii.gz`
-        searchparam.set('niftiLayers', uri)
-  
-        const newState = cvtSearchParamToState(searchparam, fetchedTemplateRootState)
-        expect(newState.ngViewerState.layers.length).toEqual(1)
-  
-        const layer = newState.ngViewerState.layers[0]
-        expect(layer.name).toEqual(uri)
-        expect(layer.source).toEqual(`nifti://${uri}`)
-        expect(layer.mixability).toEqual('nonmixable')
-      })
-    })
-
-    it('parses pluginStates correctly', () => {
-      const searchParam = new URLSearchParams()
-      searchParam.set('templateSelected', 'MNI 152 ICBM 2009c Nonlinear Asymmetric')
-      searchParam.set('parcellationSelected', 'JuBrain Cytoarchitectonic Atlas')
-      searchParam.set('pluginStates', 'http://localhost:3001/manifest.json')
-  
-      const newState = cvtSearchParamToState(searchParam, fetchedTemplateRootState)
-      expect(newState.pluginState.initManifests).toEqual([
-        ['INIT_MANIFEST_SRC', 'http://localhost:3001/manifest.json']
-      ])
-    })
-
-    it('if both standaloneVolumes and templateSelected are set, only standaloneVolumes are honoured', () => {
-      const searchParam = new URLSearchParams()
-      
-      searchParam.set('templateSelected', 'MNI 152 ICBM 2009c Nonlinear Asymmetric')
-      searchParam.set('parcellationSelected', 'JuBrain Cytoarchitectonic Atlas')
-      searchParam.set('standaloneVolumes', JSON.stringify(['nifti://http://localhost/nii.gz']))
-
-      const newState = cvtSearchParamToState(searchParam, fetchedTemplateRootState)
-      expect(newState.viewerState.templateSelected).toBeFalsy()
-      expect(newState.viewerState.parcellationSelected).toBeFalsy()
-      expect(newState.viewerState.standaloneVolumes).toEqual(['nifti://http://localhost/nii.gz'])
-    })
-  })
-
-  describe('> cvtStateToSearchParam', () => {
-
-    it('> should convert template selected', () => {
-      const { viewerState } = defaultRootState
-      const searchParam = cvtStateToSearchParam({
-        ...defaultRootState,
-        viewerState: {
-          ...viewerState,
-          templateSelected: bigbrainJson,
-        }
-      })
-
-      const stringified = searchParam.toString()
-      expect(stringified).toBe('templateSelected=Big+Brain+%28Histology%29')
-    })
-    it('> should convert template selected and parcellation selected', () => {
-  
-      const { viewerState } = defaultRootState
-      const searchParam = cvtStateToSearchParam({
-        ...defaultRootState,
-        viewerState: {
-          ...viewerState,
-          templateSelected: bigbrainJson,
-          parcellationSelected: bigbrainJson.parcellations[0]
-        }
-      })
-  
-      const stringified = searchParam.toString()
-      expect(stringified).toBe('templateSelected=Big+Brain+%28Histology%29&parcellationSelected=Cytoarchitectonic+Maps+-+v2.4')
-    })
-  })
-})
diff --git a/src/atlasViewer/atlasViewer.urlUtil.ts b/src/atlasViewer/atlasViewer.urlUtil.ts
deleted file mode 100644
index ec9bbeebf103cdec9c9c2047b0d4d255493145a1..0000000000000000000000000000000000000000
--- a/src/atlasViewer/atlasViewer.urlUtil.ts
+++ /dev/null
@@ -1,337 +0,0 @@
-import { getGetRegionFromLabelIndexId } from "src/services/effect/effect";
-import { mixNgLayers } from "src/services/state/ngViewerState.store";
-import { PLUGINSTORE_CONSTANTS } from 'src/services/state/pluginState.store'
-import { generateLabelIndexId, getNgIdLabelIndexFromRegion, IavRootStoreInterface } from "../services/stateStore.service";
-import { decodeToNumber, encodeNumber, separator } from "./atlasViewer.constantService.service";
-import { getShader, PMAP_DEFAULT_CONFIG } from "src/util/constants";
-import { viewerStateHelperStoreName } from "src/services/state/viewerState.store.helper";
-export const PARSING_SEARCHPARAM_ERROR = {
-  TEMPALTE_NOT_SET: 'TEMPALTE_NOT_SET',
-  TEMPLATE_NOT_FOUND: 'TEMPLATE_NOT_FOUND',
-  PARCELLATION_NOT_UPDATED: 'PARCELLATION_NOT_UPDATED',
-}
-const PARSING_SEARCHPARAM_WARNING = {
-  UNKNOWN_PARCELLATION: 'UNKNOWN_PARCELLATION',
-  DECODE_CIPHER_ERROR: 'DECODE_CIPHER_ERROR',
-  ID_ERROR: 'ID_ERROR'
-}
-
-export const CVT_STATE_TO_SEARCHPARAM_ERROR = {
-  TEMPLATE_NOT_SELECTED: 'TEMPLATE_NOT_SELECTED',
-}
-
-export const cvtStateToSearchParam = (state: any): URLSearchParams => {
-  const searchParam = new URLSearchParams()
-
-  const { viewerState, pluginState, uiState } = state
-  const { templateSelected, parcellationSelected, navigation, regionsSelected, standaloneVolumes } = viewerState
-
-  if (standaloneVolumes && Array.isArray(standaloneVolumes) && standaloneVolumes.length > 0) {
-    searchParam.set('standaloneVolumes', JSON.stringify(standaloneVolumes))
-  } else {
-    if (!templateSelected) { throw new Error(CVT_STATE_TO_SEARCHPARAM_ERROR.TEMPLATE_NOT_SELECTED) }
-
-    // encoding states
-    searchParam.set('templateSelected', templateSelected.name)
-    if (!!parcellationSelected) searchParam.set('parcellationSelected', parcellationSelected.name)
-  
-    // encoding selected regions
-    const accumulatorMap = new Map<string, number[]>()
-    for (const region of regionsSelected) {
-      const { ngId, labelIndex } = getNgIdLabelIndexFromRegion({ region })
-      const existingEntry = accumulatorMap.get(ngId)
-      if (existingEntry) { existingEntry.push(labelIndex) } else { accumulatorMap.set(ngId, [ labelIndex ]) }
-    }
-    const cRegionObj = {}
-    for (const [key, arr] of accumulatorMap) {
-      cRegionObj[key] = arr.map(n => encodeNumber(n)).join(separator)
-    }
-    if (Object.keys(cRegionObj).length > 0) searchParam.set('cRegionsSelected', JSON.stringify(cRegionObj))  
-  }
-  // encoding navigation
-  if (navigation) {
-    const { orientation, perspectiveOrientation, perspectiveZoom, position, zoom } = navigation
-    if (orientation && perspectiveOrientation && perspectiveZoom && position && zoom) {
-      const cNavString = [
-        orientation.map(n => encodeNumber(n, {float: true})).join(separator),
-        perspectiveOrientation.map(n => encodeNumber(n, {float: true})).join(separator),
-        encodeNumber(Math.floor(perspectiveZoom)),
-        Array.from(position).map((v: number) => Math.floor(v)).map(n => encodeNumber(n)).join(separator),
-        encodeNumber(Math.floor(zoom)),
-      ].join(`${separator}${separator}`)
-      searchParam.set('cNavigation', cNavString)
-    }
-  }
-
-  // plugin state
-  const { initManifests } = pluginState
-  const pluginStateParam = (initManifests as any[])
-    .filter(([ src ]) => src !== PLUGINSTORE_CONSTANTS.INIT_MANIFEST_SRC)
-    .map(([ _src, url]) => url)
-    .join('__')
-
-  // previewDataset state
-
-  const { previewingDatasetFiles } = uiState
-
-  if (previewingDatasetFiles && Array.isArray(previewingDatasetFiles)) {
-    const dsPrvArr = []
-    const datasetPreviews = (previewingDatasetFiles as {datasetId: string, filename: string}[])
-    for (const preview of datasetPreviews) {
-      dsPrvArr.push(preview)
-    }
-
-    if (dsPrvArr.length > 0) searchParam.set('previewingDatasetFiles', JSON.stringify(dsPrvArr))
-  }
-
-  if (initManifests.length > 0) { searchParam.set('pluginState', pluginStateParam) }
-
-  return searchParam
-}
-
-const { TEMPLATE_NOT_FOUND, TEMPALTE_NOT_SET, PARCELLATION_NOT_UPDATED } = PARSING_SEARCHPARAM_ERROR
-const { UNKNOWN_PARCELLATION, DECODE_CIPHER_ERROR, ID_ERROR } = PARSING_SEARCHPARAM_WARNING
-
-const parseSearchParamForTemplateParcellationRegion = (searchparams: URLSearchParams, state: IavRootStoreInterface, cb?: (arg: any) => void) => {
-
-
-  /**
-   * TODO if search param of either template or parcellation is incorrect, wrong things are searched
-   */
-
-
-  const templateSelected = (() => {
-    const { fetchedTemplates } = state.viewerState
-
-    const searchedName = (() => {
-      const param = searchparams.get('templateSelected')
-      if (param === 'Allen Mouse') { return `Allen adult mouse brain reference atlas V3` }
-      if (param === 'Waxholm Rat V2.0') { return 'Waxholm Space rat brain atlas v.2.0' }
-      return param
-    })()
-
-    if (!searchedName) { throw new Error(TEMPALTE_NOT_SET) }
-    const templateToLoad = fetchedTemplates.find(template => template.name === searchedName)
-    if (!templateToLoad) { throw new Error(TEMPLATE_NOT_FOUND) }
-    return templateToLoad
-  })()
-
-  const parcellationSelected = (() => {
-    const searchedName = (() => {
-      const param = searchparams.get('parcellationSelected')
-      if (param === 'Allen Mouse Brain Atlas') { return 'Allen adult mouse brain reference atlas V3 Brain Atlas' }
-      if (param === 'Whole Brain (v2.0)') { return 'Waxholm Space rat brain atlas v.2.0' }
-      return param
-    })()
-    const parcellationToLoad = templateSelected.parcellations.find(parcellation => parcellation.name === searchedName)
-    if (!parcellationToLoad) { cb && cb({ type: UNKNOWN_PARCELLATION }) }
-    return parcellationToLoad || templateSelected.parcellations[0]
-  })()
-
-  /* selected regions */
-
-  const regionsSelected = (() => {
-
-    // TODO deprecate. Fallback (defaultNgId) (should) already exist
-    // if (!viewerState.parcellationSelected.updated) throw new Error(PARCELLATION_NOT_UPDATED)
-
-    const getRegionFromlabelIndexId = getGetRegionFromLabelIndexId({ parcellation: parcellationSelected })
-    /**
-     * either or both parcellationToLoad and .regions maybe empty
-     */
-    /**
-     * backwards compatibility
-     */
-    const selectedRegionsParam = searchparams.get('regionsSelected')
-    if (selectedRegionsParam) {
-      const ids = selectedRegionsParam.split('_')
-      return ids.map(labelIndexId => getRegionFromlabelIndexId({ labelIndexId })).filter(v => !!v)
-    }
-
-    const cRegionsSelectedParam = searchparams.get('cRegionsSelected')
-    if (cRegionsSelectedParam) {
-      try {
-        const json = JSON.parse(cRegionsSelectedParam)
-
-        const selectRegionIds = []
-
-        for (const ngId in json) {
-          const val = json[ngId]
-          const labelIndicies = val.split(separator).map(n => {
-            try {
-              return decodeToNumber(n)
-            } catch (e) {
-              /**
-               * TODO poisonsed encoded char, send error message
-               */
-              cb && cb({ type: DECODE_CIPHER_ERROR, message: `cRegionSelectionParam is malformed: cannot decode ${n}` })
-              return null
-            }
-          }).filter(v => !!v)
-          for (const labelIndex of labelIndicies) {
-            selectRegionIds.push( generateLabelIndexId({ ngId, labelIndex }) )
-          }
-        }
-        return selectRegionIds
-          .map(labelIndexId => {
-            const region = getRegionFromlabelIndexId({ labelIndexId })
-            if (!region) cb && cb({ type: ID_ERROR, message: `region with id ${labelIndexId} not found, and will be ignored.` })
-            return region
-          })
-          .filter(r => !!r)
-
-      } catch (e) {
-        /**
-         * parsing cRegionSelected error
-         */
-        cb && cb({ type: DECODE_CIPHER_ERROR, message: `parsing cRegionSelected error ${e.toString()}` })
-      }
-    }
-    return []
-  })()
-
-  return {
-    templateSelected,
-    parcellationSelected,
-    regionsSelected
-  }
-}
-
-export const cvtSearchParamToState = (searchparams: URLSearchParams, state: IavRootStoreInterface, callback?: (error: any) => void): IavRootStoreInterface => {
-
-  const returnState = JSON.parse(JSON.stringify(state)) as IavRootStoreInterface
-
-  /* eslint-disable-next-line @typescript-eslint/no-empty-function */
-  const warningCb = callback || (() => {})
-
-  const { viewerState } = returnState
-
-  const searchParamStandaloneVolumes = (() => {
-    const param = searchparams.get('standaloneVolumes')
-    if (!param) {
-      return null
-    }
-    const arr = JSON.parse(param)
-    if (Array.isArray(arr)) {
-      return arr
-    }
-    else {
-      throw new Error(`param standaloneVolumes does not parse to array: ${param}`)
-    }
-  })()
-
-  if (!!searchParamStandaloneVolumes) {
-    viewerState.standaloneVolumes = searchParamStandaloneVolumes
-  } else {
-    const { templateSelected, parcellationSelected, regionsSelected } = parseSearchParamForTemplateParcellationRegion(searchparams, state, warningCb)
-    viewerState.templateSelected = templateSelected
-    viewerState.parcellationSelected = parcellationSelected
-    viewerState.regionsSelected = regionsSelected
-  }
-  
-  /* now that the parcellation is loaded, load the navigation state */
-  /* what to do with malformed navigation? */
-
-  // for backwards compatibility
-  const _viewerState = searchparams.get('navigation')
-  if (_viewerState) {
-    const [o, po, pz, p, z] = _viewerState.split('__')
-    viewerState.navigation = {
-      orientation : o.split('_').map(n => Number(n)),
-      perspectiveOrientation : po.split('_').map(n => Number(n)),
-      perspectiveZoom : Number(pz),
-      position : p.split('_').map(n => Number(n)),
-      zoom : Number(z),
-
-      // flag to allow for animation when enabled
-      animation: {},
-    }
-  }
-
-  const cViewerState = searchparams.get('cNavigation')
-  if (cViewerState) {
-    try {
-      const [ cO, cPO, cPZ, cP, cZ ] = cViewerState.split(`${separator}${separator}`)
-      const o = cO.split(separator).map(s => decodeToNumber(s, {float: true}))
-      const po = cPO.split(separator).map(s => decodeToNumber(s, {float: true}))
-      const pz = decodeToNumber(cPZ)
-      const p = cP.split(separator).map(s => decodeToNumber(s))
-      const z = decodeToNumber(cZ)
-      viewerState.navigation = {
-        orientation: o,
-        perspectiveOrientation: po,
-        perspectiveZoom: pz,
-        position: p,
-        zoom: z,
-
-        // flag to allow for animation when enabled
-        animation: {},
-      }
-    } catch (e) {
-      /**
-       * TODO Poisoned encoded char
-       * send error message
-       */
-    }
-  }
-
-  const niftiLayers = searchparams.get('niftiLayers')
-  if (niftiLayers) {
-    const layers = niftiLayers
-      .split('__')
-      .map(layer => {
-        return {
-          name : layer,
-          source : `nifti://${layer}`,
-          mixability : 'nonmixable',
-          shader : getShader(PMAP_DEFAULT_CONFIG),
-        } as any
-      })
-    const { ngViewerState } = returnState
-    ngViewerState.layers = mixNgLayers(ngViewerState.layers, layers)
-  }
-
-  const { pluginState } = returnState
-  const pluginStates = searchparams.get('pluginStates')
-  if (pluginStates) {
-    const arrPluginStates = pluginStates.split('__')
-    pluginState.initManifests = arrPluginStates.map(url => [PLUGINSTORE_CONSTANTS.INIT_MANIFEST_SRC, url] as [string, string])
-  }
-
-  const { uiState } = returnState
-  const stringSearchParam = searchparams.get('previewingDatasetFiles')
-  try {
-    if (stringSearchParam) {
-      const arr = JSON.parse(stringSearchParam) as Array<{datasetId: string, filename: string}>
-      uiState.previewingDatasetFiles = arr.map(({ datasetId, filename }) => {
-        return {
-          datasetId,
-          filename
-        }
-      })
-    }
-  } catch (e) {
-    // parsing previewingDatasetFiles
-  }
-
-  /**
-   * parsing template to get atlasId
-   */
-  (() => {
-
-    const viewreHelperState = returnState[viewerStateHelperStoreName]
-    const { templateSelected, parcellationSelected } = returnState['viewerState']
-    const { fetchedAtlases, ...rest } = viewreHelperState
-    
-    const selectedAtlas = fetchedAtlases.find(a => a['templateSpaces'].find(t => t['@id'] === (templateSelected && templateSelected['@id'])))
-    
-    const overlayLayer = selectedAtlas && selectedAtlas['parcellations'].find(p => p['@id'] === (parcellationSelected && parcellationSelected['@id']))
-
-    viewreHelperState['selectedAtlasId'] = selectedAtlas && selectedAtlas['@id']
-    viewreHelperState['overlayingAdditionalParcellations'] = (overlayLayer && !overlayLayer['baseLayer'])
-      ? [ overlayLayer ]
-      : []
-  })()
-
-  return returnState
-}
diff --git a/src/atlasViewer/atlasViewer.workerService.service.ts b/src/atlasViewer/atlasViewer.workerService.service.ts
index 17204cb05224dc78887f74cdae74ab9699ba6ec8..45ca6fd9af0cf13cda1312602551e12a3f72c6f8 100644
--- a/src/atlasViewer/atlasViewer.workerService.service.ts
+++ b/src/atlasViewer/atlasViewer.workerService.service.ts
@@ -1,9 +1,11 @@
 import { Injectable } from "@angular/core";
 import { fromEvent } from "rxjs";
 import { filter, take } from "rxjs/operators";
+import { getUuid } from "src/util/fn";
 
 /* telling webpack to pack the worker file */
-import '../util/worker.js'
+import '../../worker/worker.js'
+import '../../worker/worker-plotly.js'
 
 /**
  * export the worker, so that services that does not require dependency injection can import the worker
@@ -24,7 +26,7 @@ export class AtlasWorkerService {
 
   async sendMessage(data: IWorkerMessage){
 
-    const newUuid = crypto.getRandomValues(new Uint32Array(1))[0].toString(16)
+    const newUuid = getUuid()
     this.worker.postMessage({
       id: newUuid,
       ...data
diff --git a/src/atlasViewer/modalUnit/modalUnit.component.ts b/src/atlasViewer/modalUnit/modalUnit.component.ts
deleted file mode 100644
index eb62a94bb383b8a17e0a3d9d145872e23b0cede0..0000000000000000000000000000000000000000
--- a/src/atlasViewer/modalUnit/modalUnit.component.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { Component, Input, TemplateRef, ViewChild, ViewContainerRef } from '@angular/core'
-
-@Component({
-  templateUrl : './modalUnit.template.html',
-  styleUrls : [
-    './modalUnit.style.css',
-  ],
-})
-
-export class ModalUnit {
-  @Input() public title: string
-  @Input() public body: string = 'Modal Body Text'
-  @Input() public template: TemplateRef<any>
-  @Input() public footer: string
-
-  @ViewChild('templateContainer', {read: ViewContainerRef}) public templateContainer: ViewContainerRef
-
-  constructor(public viewContainerRef: ViewContainerRef) {
-
-  }
-
-  public ngAfterViewInit() {
-    if (this.templateContainer) {
-      this.templateContainer.createEmbeddedView(this.template)
-    }
-  }
-}
diff --git a/src/atlasViewer/modalUnit/modalUnit.style.css b/src/atlasViewer/modalUnit/modalUnit.style.css
deleted file mode 100644
index 619233185073476c359eb2b6188f1a5280a166c2..0000000000000000000000000000000000000000
--- a/src/atlasViewer/modalUnit/modalUnit.style.css
+++ /dev/null
@@ -1,8 +0,0 @@
-:host-context(.darktheme) div.modal-header,
-:host-context(.darktheme) div.modal-body,
-:host-context(.darktheme) div.modal-footer
-{
-  background-color:rgba(50,50,50,1.0);
-  border-color: rgba(0,0,0,0.2);
-  color:white;
-}
\ No newline at end of file
diff --git a/src/atlasViewer/modalUnit/modalUnit.template.html b/src/atlasViewer/modalUnit/modalUnit.template.html
deleted file mode 100644
index a3aba585c53b1d35e044ec5aa92ece92c94a05a2..0000000000000000000000000000000000000000
--- a/src/atlasViewer/modalUnit/modalUnit.template.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<div *ngIf = "title" class = "modal-header">
-  <h4 class = "modal-title pull-left">
-    {{ title }}
-  </h4>
-</div>
-
-<div *ngIf = "!template" class = "modal-body">
-  {{ body }}
-</div>
-
-<div *ngIf = "template" class="modal-body">
-  <ng-template #templateContainer>
-
-  </ng-template>
-</div>
-
-<div *ngIf = "footer" class = "modal-footer">
-  {{ footer }}
-</div>
\ No newline at end of file
diff --git a/src/atlasViewer/mouseOver.directive.ts b/src/atlasViewer/mouseOver.directive.ts
deleted file mode 100644
index 7938d4a36c1ef02ce7431565426a34af42af7b62..0000000000000000000000000000000000000000
--- a/src/atlasViewer/mouseOver.directive.ts
+++ /dev/null
@@ -1,250 +0,0 @@
-import { Directive, Pipe, PipeTransform, SecurityContext } from "@angular/core";
-import { DomSanitizer, SafeHtml } from "@angular/platform-browser";
-import { select, Store } from "@ngrx/store";
-import { combineLatest, merge, Observable } from "rxjs";
-import { distinctUntilChanged, filter, map, scan, shareReplay, startWith, withLatestFrom } from "rxjs/operators";
-import { TransformOnhoverSegmentPipe } from "src/atlasViewer/onhoverSegment.pipe";
-import { LoggingService } from "src/logging";
-import { uiStateMouseOverSegmentsSelector, uiStateMouseoverUserLandmark } from "src/services/state/uiState/selectors";
-import { getNgIdLabelIndexFromId } from "src/services/stateStore.service";
-
-/**
- * Scan function which prepends newest positive (i.e. defined) value
- *
- * e.g. const source = new Subject()
- * source.pipe(
- *  scan(temporalPositveScanFn, [])
- * ).subscribe(this.log.log) // outputs
- *
- *
- *
- */
-export const temporalPositveScanFn = (acc: Array<{segments: any, landmark: any, userLandmark: any}>, curr: {segments: any, landmark: any, userLandmark: any}) => {
-
-  const keys = Object.keys(curr)
-
-  // empty array is truthy
-  const isPositive = keys.some(key => Array.isArray(curr[key])
-    ? curr[key].length > 0
-    : !!curr[key]
-  )
-
-  return isPositive
-    ? [curr, ...(acc.filter(item => !keys.some(key => !!item[key])))] as Array<{segments?: any, landmark?: any, userLandmark?: any}>
-    : acc.filter(item => !keys.some(key => !!item[key]))
-}
-
-@Directive({
-  selector: '[iav-mouse-hover]',
-  exportAs: 'iavMouseHover',
-})
-
-export class MouseHoverDirective {
-
-  public onHoverObs$: Observable<{segments: any, landmark: any, userLandmark: any}>
-  public currentOnHoverObs$: Observable<{segments: any, landmark: any, userLandmark: any}>
-
-  constructor(
-    private store$: Store<any>,
-    private log: LoggingService,
-  ) {
-
-    // TODO consider moving these into a single obs serviced by a DI service
-    // can potentially net better performance
-
-    const onHoverUserLandmark$ = this.store$.pipe(
-      select(uiStateMouseoverUserLandmark)
-    )
-
-    const onHoverLandmark$ = combineLatest(
-      this.store$.pipe(
-        select('uiState'),
-        select('mouseOverLandmark'),
-      ),
-      this.store$.pipe(
-        select('dataStore'),
-        select('fetchedSpatialData'),
-        startWith([]),
-      ),
-    ).pipe(
-      map(([landmark, spatialDatas]) => {
-        if (landmark === null) { return landmark }
-        const idx = Number(landmark.replace('label=', ''))
-        if (isNaN(idx)) {
-          this.log.warn(`Landmark index could not be parsed as a number: ${landmark}`)
-          return {
-            landmarkName: idx,
-          }
-        } else {
-          return {
-            ...spatialDatas[idx],
-            landmarkName: spatialDatas[idx].name,
-          }
-        }
-      }),
-    )
-
-    const onHoverSegments$ = this.store$.pipe(
-      select(uiStateMouseOverSegmentsSelector),
-      filter(v => !!v),
-      withLatestFrom(
-        this.store$.pipe(
-          select('viewerState'),
-          select('parcellationSelected'),
-          startWith(null),
-        ),
-      ),
-      map(([ arr, parcellationSelected ]) => parcellationSelected && parcellationSelected.auxillaryMeshIndices
-        ? arr.filter(({ segment }) => {
-          // if segment is not a string (i.e., not labelIndexId) return true
-          if (typeof segment !== 'string') { return true }
-          const { labelIndex } = getNgIdLabelIndexFromId({ labelIndexId: segment })
-          return parcellationSelected.auxillaryMeshIndices.indexOf(labelIndex) < 0
-        })
-        : arr),
-      distinctUntilChanged((o, n) => o.length === n.length
-        && n.every(segment =>
-          o.find(oSegment => oSegment.layer.name === segment.layer.name
-            && oSegment.segment === segment.segment))),
-    )
-
-    const mergeObs = merge(
-      onHoverSegments$.pipe(
-        distinctUntilChanged(),
-        map(segments => {
-          return { segments }
-        }),
-      ),
-      onHoverLandmark$.pipe(
-        distinctUntilChanged(),
-        map(landmark => {
-          return { landmark }
-        }),
-      ),
-      onHoverUserLandmark$.pipe(
-        distinctUntilChanged(),
-        map(userLandmark => {
-          return { userLandmark }
-        }),
-      ),
-    ).pipe(
-      shareReplay(1),
-    )
-
-    this.onHoverObs$ = mergeObs.pipe(
-      scan((acc, curr) => {
-        return {
-          ...acc,
-          ...curr,
-        }
-      }, { segments: null, landmark: null, userLandmark: null }),
-      shareReplay(1),
-    )
-
-    this.currentOnHoverObs$ = mergeObs.pipe(
-      scan(temporalPositveScanFn, []),
-      map(arr => {
-
-        let returnObj = {
-          segments: null,
-          landmark: null,
-          userLandmark: null,
-        }
-
-        for (const val of arr) {
-          returnObj = {
-            ...returnObj,
-            ...val
-          }
-        }
-
-        return returnObj
-      }),
-      shareReplay(1),
-    )
-  }
-}
-
-@Pipe({
-  name: 'mouseOverTextPipe',
-})
-
-export class MouseOverTextPipe implements PipeTransform {
-
-  private transformOnHoverSegmentPipe: TransformOnhoverSegmentPipe
-  constructor(private sanitizer: DomSanitizer) {
-    this.transformOnHoverSegmentPipe = new TransformOnhoverSegmentPipe(this.sanitizer)
-  }
-
-  private renderText = ({ label, obj }): SafeHtml[] => {
-    switch (label) {
-    case 'landmark': {
-      const { dataset = [] } = obj
-      return [
-        this.sanitizer.sanitize(SecurityContext.HTML, obj.landmarkName),
-        ...(dataset.map(ds => this.sanitizer.bypassSecurityTrustHtml(`
-<span class="text-muted">
-  ${this.sanitizer.sanitize(SecurityContext.HTML, ds.name)}
-</span>
-`)))
-      ]
-    }
-    case 'segments':
-      return obj.map(({ segment }) => this.transformOnHoverSegmentPipe.transform(segment))
-    case 'userLandmark':
-      return [this.sanitizer.sanitize(SecurityContext.HTML, obj.name)]
-    default:
-      // ts-lint:disable-next-line
-      console.warn(`mouseOver.directive.ts#mouseOverTextPipe: Cannot be displayed: label: ${label}`)
-      return [this.sanitizer.bypassSecurityTrustHtml(`Cannot be displayed: label: ${label}`)]
-    }
-  }
-
-  public transform(inc: {segments: any, landmark: any, userLandmark: any}): Array<{label: string, text: SafeHtml[]}> {
-    const keys = Object.keys(inc)
-    return keys
-      // if is segments, filter out if lengtth === 0
-      .filter(key => Array.isArray(inc[key]) ? inc[key].length > 0 : true )
-      // for other properties, check if value is defined
-      .filter(key => !!inc[key])
-      .map(key => {
-        return {
-          label: key,
-          text: this.renderText({ label: key, obj: inc[key] })
-        }
-      })
-  }
-}
-
-@Pipe({
-  name: 'mouseOverIconPipe',
-})
-
-export class MouseOverIconPipe implements PipeTransform {
-
-  public transform(type: string): {fontSet: string, fontIcon: string} {
-
-    switch (type) {
-    case 'landmark':
-      return {
-        fontSet: 'fas',
-        fontIcon: 'fa-map-marker-alt',
-      }
-    case 'segments':
-      return {
-        fontSet: 'fas',
-        fontIcon: 'fa-brain',
-      }
-    case 'userLandmark':
-      return {
-        fontSet: 'fas',
-        fontIcon: 'fa-map-marker-alt',
-      }
-    default:
-      return {
-        fontSet: 'fas',
-        fontIcon: 'fa-file',
-      }
-    }
-  }
-}
diff --git a/src/atlasViewer/pluginUnit/atlasViewer.pluginService.service.spec.ts b/src/atlasViewer/pluginUnit/atlasViewer.pluginService.service.spec.ts
deleted file mode 100644
index acb0d9425edc4d4e54b72ab59ce658f9448f65b1..0000000000000000000000000000000000000000
--- a/src/atlasViewer/pluginUnit/atlasViewer.pluginService.service.spec.ts
+++ /dev/null
@@ -1,110 +0,0 @@
-// import { PluginServices } from "./atlasViewer.pluginService.service";
-// import { TestBed, inject } from "@angular/core/testing";
-// import { MainModule } from "src/main.module";
-// import { HttpTestingController, HttpClientTestingModule } from '@angular/common/http/testing'
-
-// const MOCK_PLUGIN_MANIFEST = {
-//   name: 'fzj.xg.MOCK_PLUGIN_MANIFEST',
-//   templateURL: 'http://localhost:10001/template.html',
-//   scriptURL: 'http://localhost:10001/script.js'
-// }
-
-// describe('PluginServices', () => {
-//   let pluginService: PluginServices
-
-//   beforeEach(async () => {
-//     await TestBed.configureTestingModule({
-//       imports: [
-//         HttpClientTestingModule,
-//         MainModule
-//       ]
-//     }).compileComponents()
-
-//     pluginService = TestBed.get(PluginServices)
-//   })
-
-//   it(
-//     'is instantiated in test suite OK',
-//     () => expect(TestBed.get(PluginServices)).toBeTruthy()
-//   )
-
-//   it(
-//     'expectOne is working as expected',
-//     inject([HttpTestingController], (httpMock: HttpTestingController) => {
-//       expect(httpMock.match('test').length).toBe(0)
-//       pluginService.fetch('test')
-//       expect(httpMock.match('test').length).toBe(1)
-//       pluginService.fetch('test')
-//       pluginService.fetch('test')
-//       expect(httpMock.match('test').length).toBe(2)
-//     })
-//   )
-
-//   describe('#launchPlugin', () => {
-
-//     describe('basic fetching functionality', () => {
-//       it(
-//         'fetches templateURL and scriptURL properly',
-//         inject([HttpTestingController], (httpMock: HttpTestingController) => {
-
-//           pluginService.launchPlugin(MOCK_PLUGIN_MANIFEST)
-
-//           const mockTemplate = httpMock.expectOne(MOCK_PLUGIN_MANIFEST.templateURL)
-//           const mockScript = httpMock.expectOne(MOCK_PLUGIN_MANIFEST.scriptURL)
-
-//           expect(mockTemplate).toBeTruthy()
-//           expect(mockScript).toBeTruthy()
-//         })
-//       )
-//       it(
-//         'template overrides templateURL',
-//         inject([HttpTestingController], (httpMock: HttpTestingController) => {
-//           pluginService.launchPlugin({
-//             ...MOCK_PLUGIN_MANIFEST,
-//             template: ''
-//           })
-
-//           httpMock.expectNone(MOCK_PLUGIN_MANIFEST.templateURL)
-//           const mockScript = httpMock.expectOne(MOCK_PLUGIN_MANIFEST.scriptURL)
-
-//           expect(mockScript).toBeTruthy()
-//         })
-//       )
-
-//       it(
-//         'script overrides scriptURL',
-
-//         inject([HttpTestingController], (httpMock: HttpTestingController) => {
-//           pluginService.launchPlugin({
-//             ...MOCK_PLUGIN_MANIFEST,
-//             script: ''
-//           })
-
-//           const mockTemplate = httpMock.expectOne(MOCK_PLUGIN_MANIFEST.templateURL)
-//           httpMock.expectNone(MOCK_PLUGIN_MANIFEST.scriptURL)
-
-//           expect(mockTemplate).toBeTruthy()
-//         })
-//       )
-//     })
-
-//     describe('racing slow cconnection when launching plugin', () => {
-//       it(
-//         'when template/script has yet been fetched, repeated launchPlugin should not result in repeated fetching',
-//         inject([HttpTestingController], (httpMock:HttpTestingController) => {
-
-//           expect(pluginService.pluginIsLaunching(MOCK_PLUGIN_MANIFEST.name)).toBeFalsy()
-//           pluginService.launchPlugin(MOCK_PLUGIN_MANIFEST)
-//           pluginService.launchPlugin(MOCK_PLUGIN_MANIFEST)
-//           expect(httpMock.match(MOCK_PLUGIN_MANIFEST.scriptURL).length).toBe(1)
-//           expect(httpMock.match(MOCK_PLUGIN_MANIFEST.templateURL).length).toBe(1)
-
-//           expect(pluginService.pluginIsLaunching(MOCK_PLUGIN_MANIFEST.name)).toBeTruthy()
-//         })
-//       )
-//     })
-//   })
-// })
-
-// TODO currently crashes test somehow
-// TODO figure out why
diff --git a/src/atlasViewer/pluginUnit/atlasViewer.pluginService.service.ts b/src/atlasViewer/pluginUnit/atlasViewer.pluginService.service.ts
deleted file mode 100644
index dff1231817ccc94cd2e8f59b0f5ed5fcd1765179..0000000000000000000000000000000000000000
--- a/src/atlasViewer/pluginUnit/atlasViewer.pluginService.service.ts
+++ /dev/null
@@ -1,306 +0,0 @@
-import { HttpClient } from '@angular/common/http'
-import { ComponentFactory, ComponentFactoryResolver, Injectable, ViewContainerRef, Inject, InjectionToken } from "@angular/core";
-import { PLUGINSTORE_ACTION_TYPES } from "src/services/state/pluginState.store";
-import { IavRootStoreInterface, isDefined } from 'src/services/stateStore.service'
-import { PluginUnit } from "./pluginUnit.component";
-import { select, Store } from "@ngrx/store";
-import { BehaviorSubject, merge, Observable, of, Subject, zip } from "rxjs";
-import { filter, map, shareReplay, switchMap, catchError } from "rxjs/operators";
-import { LoggingService } from 'src/logging';
-import { PluginHandler } from 'src/util/pluginHandler';
-import { WidgetUnit, WidgetServices } from "src/widget";
-import { APPEND_SCRIPT_TOKEN, REMOVE_SCRIPT_TOKEN, BACKENDURL, getHttpHeader } from 'src/util/constants';
-import { PluginFactoryDirective } from './pluginFactory.directive';
-
-export const registerPluginFactoryDirectiveFactory = (pSer: PluginServices) => {
-  return (pFactoryDirective: PluginFactoryDirective) => {
-    pSer.loadExternalLibraries = pFactoryDirective.loadExternalLibraries.bind(pFactoryDirective)
-    pSer.unloadExternalLibraries = pFactoryDirective.unloadExternalLibraries.bind(pFactoryDirective)
-    pSer.pluginViewContainerRef = pFactoryDirective.viewContainerRef
-  }
-}
-
-@Injectable({
-  providedIn : 'root',
-})
-
-export class PluginServices {
-
-  public pluginHandlersMap: Map<string, PluginHandler> = new Map()
-
-  public loadExternalLibraries: (libraries: string[]) => Promise<any> = () => Promise.reject(`fail to overwritten`)
-  public unloadExternalLibraries: (libraries: string[]) => void = () => { throw new Error(`failed to be overwritten`) }
-
-  public fetchedPluginManifests: IPluginManifest[] = []
-  public pluginViewContainerRef: ViewContainerRef
-
-  private pluginUnitFactory: ComponentFactory<PluginUnit>
-  public minimisedPlugins$: Observable<Set<string>>
-
-  /**
-   * TODO remove polyfil and convert all calls to this.fetch to http client
-   */
-  public fetch: (url: string, httpOption?: any) => Promise<any> = (url, httpOption = {}) => this.http.get(url, httpOption).toPromise()
-
-  constructor(
-    private widgetService: WidgetServices,
-    private cfr: ComponentFactoryResolver,
-    private store: Store<IavRootStoreInterface>,
-    private http: HttpClient,
-    private log: LoggingService,
-    @Inject(APPEND_SCRIPT_TOKEN) private appendSrc: (src: string) => Promise<HTMLScriptElement>,
-    @Inject(REMOVE_SCRIPT_TOKEN) private removeSrc: (src: HTMLScriptElement) => void,
-  ) {
-
-    // TODO implement
-    this.store.pipe(
-      select('pluginState'),
-      select('initManifests'),
-      filter(v => !!v),
-    )
-
-    this.pluginUnitFactory = this.cfr.resolveComponentFactory( PluginUnit )
-
-    /**
-     * TODO convert to rxjs streams, instead of Promise.all
-     */
-
-    const pluginUrl = `${BACKENDURL.replace(/\/$/,'')}/plugins`
-    const streamFetchedManifests$ = this.http.get(pluginUrl,{
-      responseType: 'json',
-      headers: getHttpHeader(),
-    }).pipe(
-      switchMap((arr: string[]) => {
-        return zip(
-          ...arr.map(url => this.http.get(url, {
-            responseType: 'json',
-            headers: getHttpHeader()
-          }).pipe(
-            catchError((err, caught) => of(null))
-          ))
-        ).pipe(
-          map(arr => arr.filter(v => !!v))
-        )
-      })
-    )
-
-    streamFetchedManifests$.subscribe(
-      arr => {
-        this.fetchedPluginManifests = arr
-        this.log.log(this.fetchedPluginManifests)
-      },
-      this.log.error,
-      () => {
-        this.log.log(`fetching end`)
-      }
-    )
-
-    this.minimisedPlugins$ = merge(
-      of(new Set()),
-      this.widgetService.minimisedWindow$,
-    ).pipe(
-      map(set => {
-        const returnSet = new Set<string>()
-        for (const [pluginName, wu] of this.mapPluginNameToWidgetUnit) {
-          if (set.has(wu)) {
-            returnSet.add(pluginName)
-          }
-        }
-        return returnSet
-      }),
-      shareReplay(1),
-    )
-
-    this.launchedPlugins$ = new BehaviorSubject(new Set())
-  }
-
-  public launchNewWidget = (manifest) => this.launchPlugin(manifest)
-    .then(handler => {
-      this.orphanPlugins.add(manifest)
-      handler.onShutdown(() => {
-        this.orphanPlugins.delete(manifest)
-      })
-    })
-
-  public readyPlugin(plugin: IPluginManifest): Promise<any> {
-    return Promise.all([
-      isDefined(plugin.template)
-        ? Promise.resolve()
-        : isDefined(plugin.templateURL)
-          ? this.fetch(plugin.templateURL, {responseType: 'text'}).then(template => plugin.template = template)
-          : Promise.reject('both template and templateURL are not defined') ,
-      isDefined(plugin.scriptURL) ? Promise.resolve() : Promise.reject(`inline script has been deprecated. use scriptURL instead`),
-    ])
-  }
-
-  private launchedPlugins: Set<string> = new Set()
-  public launchedPlugins$: BehaviorSubject<Set<string>>
-  public pluginHasLaunched(pluginName: string) {
-    return this.launchedPlugins.has(pluginName)
-  }
-  public addPluginToLaunchedSet(pluginName: string) {
-    this.launchedPlugins.add(pluginName)
-    this.launchedPlugins$.next(this.launchedPlugins)
-  }
-  public removePluginFromLaunchedSet(pluginName: string) {
-    this.launchedPlugins.delete(pluginName)
-    this.launchedPlugins$.next(this.launchedPlugins)
-  }
-
-  public pluginIsLaunching(pluginName: string) {
-    return this.launchingPlugins.has(pluginName)
-  }
-  public addPluginToIsLaunchingSet(pluginName: string) {
-    this.launchingPlugins.add(pluginName)
-  }
-  public removePluginFromIsLaunchingSet(pluginName: string) {
-    this.launchingPlugins.delete(pluginName)
-  }
-
-  private mapPluginNameToWidgetUnit: Map<string, WidgetUnit> = new Map()
-
-  public pluginIsMinimised(pluginName: string) {
-    return this.widgetService.isMinimised( this.mapPluginNameToWidgetUnit.get(pluginName) )
-  }
-
-  private launchingPlugins: Set<string> = new Set()
-  public orphanPlugins: Set<IPluginManifest> = new Set()
-  public launchPlugin(plugin: IPluginManifest) {
-    if (this.pluginIsLaunching(plugin.name)) {
-      // plugin launching please be patient
-      // TODO add visual feedback
-      return
-    }
-    if ( this.pluginHasLaunched(plugin.name)) {
-      // plugin launched
-      // TODO add visual feedback
-
-      // if widget window is minimized, maximize it
-
-      const wu = this.mapPluginNameToWidgetUnit.get(plugin.name)
-      if (this.widgetService.isMinimised(wu)) {
-        this.widgetService.unminimise(wu)
-      } else {
-        this.widgetService.minimise(wu)
-      }
-      return
-    }
-
-    this.addPluginToIsLaunchingSet(plugin.name)
-
-    return this.readyPlugin(plugin)
-      .then(async () => {
-        const pluginUnit = this.pluginViewContainerRef.createComponent( this.pluginUnitFactory )
-        /* TODO in v0.2, I used:
-
-        const template = document.createElement('div')
-        template.insertAdjacentHTML('afterbegin',template)
-
-        // reason was:
-        // changed from innerHTML to insertadjacenthtml to accomodate angular elements ... not too sure about the actual ramification
-
-        */
-
-        const handler = new PluginHandler()
-        this.pluginHandlersMap.set(plugin.name, handler)
-
-        /**
-         * define the handler properties prior to appending plugin script
-         * so that plugin script can access properties w/o timeout
-         */
-        handler.initState = plugin.initState
-          ? plugin.initState
-          : null
-
-        handler.initStateUrl = plugin.initStateUrl
-          ? plugin.initStateUrl
-          : null
-
-        handler.setInitManifestUrl = (url) => this.store.dispatch({
-          type : PLUGINSTORE_ACTION_TYPES.SET_INIT_PLUGIN,
-          manifest : {
-            name : plugin.name,
-            initManifestUrl : url,
-          },
-        })
-
-        const shutdownCB = [
-          () => {
-            this.removePluginFromLaunchedSet(plugin.name)
-          },
-        ]
-
-        handler.onShutdown = (cb) => {
-          if (typeof cb !== 'function') {
-            this.log.warn('onShutdown requires the argument to be a function')
-            return
-          }
-          shutdownCB.push(cb)
-        }
-
-        const scriptEl = await this.appendSrc(plugin.scriptURL)
-        handler.onShutdown(() => this.removeSrc(scriptEl))
-
-        const template = document.createElement('div')
-        template.insertAdjacentHTML('afterbegin', plugin.template)
-        pluginUnit.instance.elementRef.nativeElement.append( template )
-
-        const widgetCompRef = this.widgetService.addNewWidget(pluginUnit, {
-          state : 'floating',
-          exitable : true,
-          persistency: plugin.persistency,
-          title : plugin.displayName || plugin.name,
-        })
-
-        this.addPluginToLaunchedSet(plugin.name)
-        this.removePluginFromIsLaunchingSet(plugin.name)
-
-        this.mapPluginNameToWidgetUnit.set(plugin.name, widgetCompRef.instance)
-
-        const unsubscribeOnPluginDestroy = []
-
-        // TODO deprecate sec
-        handler.blink = (_sec?: number) => {
-          widgetCompRef.instance.blinkOn = true
-        }
-
-        handler.setProgressIndicator = (val) => widgetCompRef.instance.progressIndicator = val
-
-        handler.shutdown = () => {
-          widgetCompRef.instance.exit()
-        }
-
-        handler.onShutdown(() => {
-          unsubscribeOnPluginDestroy.forEach(s => s.unsubscribe())
-          this.pluginHandlersMap.delete(plugin.name)
-          this.mapPluginNameToWidgetUnit.delete(plugin.name)
-        })
-
-        pluginUnit.onDestroy(() => {
-          while (shutdownCB.length > 0) {
-            shutdownCB.pop()()
-          }
-        })
-
-        return handler
-      })
-  }
-}
-
-export interface IPluginManifest {
-  name?: string
-  displayName?: string
-  templateURL?: string
-  template?: string
-  scriptURL?: string
-  script?: string
-  initState?: any
-  initStateUrl?: string
-  persistency?: boolean
-
-  description?: string
-  desc?: string
-
-  homepage?: string
-  authors?: string
-}
diff --git a/src/atlasViewer/pluginUnit/index.ts b/src/atlasViewer/pluginUnit/index.ts
deleted file mode 100644
index cc1c5058b8b8133b2d2c1e8547b6c9c51404a752..0000000000000000000000000000000000000000
--- a/src/atlasViewer/pluginUnit/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { PluginServices, IPluginManifest } from './atlasViewer.pluginService.service'
\ No newline at end of file
diff --git a/src/atlasViewer/pluginUnit/plugin_styles.css b/src/atlasViewer/pluginUnit/plugin_styles.css
deleted file mode 100644
index dea00063b288c84bc44229f447abd358aaf82552..0000000000000000000000000000000000000000
--- a/src/atlasViewer/pluginUnit/plugin_styles.css
+++ /dev/null
@@ -1,69 +0,0 @@
-/* layout */
-
-[plugincontainer] .btn,
-[plugincontainer] .input-group-addon,
-[plugincontainer] input[type="text"],
-[plugincontainer] .panel
-{
-  border-radius:0px;
-  border:none;
-}
-
-[plugincontainer] .btn
-{
-  opacity : 0.9;
-  transition: opacity 0.3s ease, transform 0.3s ease;
-  box-shadow : rgba(5, 5, 5, 0.1) 0px 4px 6px 0px;
-}
-
-[plugincontainer] .btn:hover
-{
-  opacity:1.0;
-  transform:translateY(-5%);
-  box-shadow : rgba(5, 5, 5, 0.25) 0px 4px 6px 0px;
-}
-
-[plugincontainer] .form-control
-{
-  background:none;
-  border:none;
-}
-
-/* colour */
-[darktheme="true"] [plugincontainer] .btn,
-[darktheme="true"] [plugincontainer] input[type="text"],
-[darktheme="true"] [plugincontainer] input[type="number"],
-[darktheme="true"] [plugincontainer] .panel
-{
-  background-color:rgba(81,81,81,0.8);
-  color:rgba(255,255,255,0.8);
-}
-
-[darktheme="true"] [plugincontainer] .input-group-addon
-{
-  background-color:rgba(60,60,60,0.2);
-  color:rgba(255,255,255,0.8);
-  /* border-top:1px solid rgba(255,255,255,0.05);
-  border-left:1px solid rgba(255,255,255,0.05);
-  border-right:1px solid rgba(255,255,255,0.05); */
-}
-
-[darktheme="true"] [plugincontainer] hr
-{
-  border-color:rgba(100,100,100,0.5);
-}
-
-[darktheme="true"] [plugincontainer] .btn.btn-active
-{
-  background-color:rgba(65,65,65,0.8);
-  transform: translateY(2px);
-  color:rgba(255,255,255,1.0);
-  box-shadow: inset 0 2px 2px -2px rgba(0,0,0,0.2);
-}
-
-[darktheme="true"] .alert.alert-danger
-{
-  color: #f2dede;
-  background-color: rgba(169, 68, 66, 0.5);
-  border-color: rgba(169, 68, 66,0.2)
-}
diff --git a/src/auth/auth.directive.ts b/src/auth/auth.directive.ts
index 982a57b3d0a82945c38079b5bcd21dde0ff2cfc7..94d64d7e6da8f090014553537bca7ca040a48132 100644
--- a/src/auth/auth.directive.ts
+++ b/src/auth/auth.directive.ts
@@ -3,7 +3,7 @@ import { Observable } from "rxjs";
 import { IUser, AuthService } from './auth.service'
 
 @Directive({
-  selector: '[iav-auth-authState]',
+  selector: '[iav-auth-auth-state]',
   exportAs: 'iavAuthAuthState'
 })
 
diff --git a/src/auth/auth.service.spec.ts b/src/auth/auth.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..504e7299ca852190d15e4218da246f132bbc6315
--- /dev/null
+++ b/src/auth/auth.service.spec.ts
@@ -0,0 +1,88 @@
+import { HttpClientTestingModule, HttpTestingController } from "@angular/common/http/testing"
+import { TestBed } from "@angular/core/testing"
+import { hot } from "jasmine-marbles"
+import { PureContantService } from "src/util"
+import { AuthService } from "./auth.service"
+
+describe('>auth.service.ts', () => {
+  describe('> AuthService', () => {
+    beforeEach(() => {
+      TestBed.configureTestingModule({
+        imports: [
+          HttpClientTestingModule
+        ],
+        providers: [
+          AuthService,
+          {
+            provide: PureContantService,
+            useValue: {
+              backendUrl: `http://localhost:3000/`
+            }
+          }
+        ]
+      })
+    })
+
+    describe('> user$', () => {
+      let ctrl: HttpTestingController
+      let authService: AuthService
+
+      afterEach(() => {
+        ctrl.verify()
+      })
+      beforeEach(() => {
+
+        ctrl = TestBed.inject(HttpTestingController)
+        authService = TestBed.inject(AuthService)
+      })
+
+      it('> if http response errors, user$ should be stream of null', () => {
+
+        const resp = ctrl.expectOne('http://localhost:3000/user')
+        resp.error(null, {
+          status: 404,
+        })
+        expect(
+          authService.user$
+        ).toBeObservable(
+          hot('(a|)', {
+            a: null
+          })
+        )
+      })
+
+      it('> if http response contains truthy error key, user should return stream of null', () => {
+
+        const resp = ctrl.expectOne('http://localhost:3000/user')
+        resp.flush({
+          error: true
+        })
+        expect(
+          authService.user$
+        ).toBeObservable(
+          hot('(a|)', {
+            a: null
+          })
+        )
+      })
+
+      it('> if http response does not contain error key, should return the resp', () => {
+        
+        const mockUser = {
+          name: 'foobar',
+          id: 'baz'
+        }
+        const resp = ctrl.expectOne('http://localhost:3000/user')
+        resp.flush(mockUser)
+
+        expect(
+          authService.user$
+        ).toBeObservable(
+          hot('(a|)', {
+            a: mockUser
+          })
+        )
+      })
+    })
+  })
+})
diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts
index 12720ca923e6bd1399deeaecdd8475f5ef9c400e..ae56dcc710806d93c05b0803cc42f0ac656accb1 100644
--- a/src/auth/auth.service.ts
+++ b/src/auth/auth.service.ts
@@ -1,10 +1,24 @@
 import { HttpClient } from "@angular/common/http";
 import { Injectable, OnDestroy } from "@angular/core";
 import { Observable, of, Subscription } from "rxjs";
-import { catchError, shareReplay, mapTo } from "rxjs/operators";
+import { catchError, map, shareReplay } from "rxjs/operators";
+import { PureContantService } from "src/util";
+import { BACKENDURL } from "src/util/constants";
 
 const IV_REDIRECT_TOKEN = `IV_REDIRECT_TOKEN`
 
+export type TUserRouteResp = TUserRoute & TUserRouteError
+
+export type TUserRoute = {
+  id: string
+}
+
+export type TUserRouteError = {
+  error: boolean
+  message?: string
+}
+
+
 @Injectable({
   providedIn: 'root',
 })
@@ -25,8 +39,17 @@ export class AuthService implements OnDestroy {
     href: 'hbp-oidc-v2/auth'
   }]
 
-  constructor(private httpClient: HttpClient) {
-    this.user$ = this.httpClient.get('user').pipe(
+  constructor(
+    private httpClient: HttpClient,
+    private constantSvc: PureContantService,
+  ) {
+    this.user$ = this.httpClient.get<TUserRouteResp>(`${this.constantSvc.backendUrl}user`).pipe(
+      map(json => {
+        if (json.error) {
+          throw new Error(json.message || 'User not loggedin.')
+        }
+        return json
+      }),
       catchError(_err => {
         return of(null)
       }),
diff --git a/src/components/confirmDialog/confirmDialog.component.spec.ts b/src/components/confirmDialog/confirmDialog.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..535e6f9fb002158127e992a8bedd650eb6b3425c
--- /dev/null
+++ b/src/components/confirmDialog/confirmDialog.component.spec.ts
@@ -0,0 +1,54 @@
+import { CommonModule } from "@angular/common"
+import { TestBed, async } from "@angular/core/testing"
+import { MAT_DIALOG_DATA } from "@angular/material/dialog"
+import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module"
+import { ComponentsModule } from "../components.module"
+import { ConfirmDialogComponent } from "./confirmDialog.component"
+
+describe('> confirmDialog.component.spec.ts', () => {
+
+  describe('> ConfirmDialogComponent', () => {
+    let matDialogData = {}
+    beforeEach(async(() => {
+      TestBed.configureTestingModule({
+        imports: [
+          AngularMaterialModule,
+          CommonModule,
+          ComponentsModule,
+        ],
+        providers: [{
+          provide: MAT_DIALOG_DATA,
+          useFactory: () => {
+            return matDialogData as any
+          }
+        }]
+      }).compileComponents()
+    }))
+
+    it('> can be created', () => {
+      const fixutre = TestBed.createComponent(ConfirmDialogComponent)
+      expect(fixutre).toBeTruthy()
+    })
+
+    describe('> if both markdown and message are truthy', () => {
+      beforeEach(() => {
+        matDialogData = {
+          markdown: `hello world`,
+          message: `foo bar`,
+        }
+      })
+      it('> should show markdown in preference', () => {
+        const fixture = TestBed.createComponent(ConfirmDialogComponent)
+        fixture.detectChanges()
+        const text = fixture.debugElement.nativeElement.textContent
+        expect(
+          /hello\sworld/.test(text)
+        ).toBeTruthy()
+        expect(
+          /foo\sbar/.test(text)
+        ).toBeFalsy()
+      })
+    })
+  })
+
+})
\ No newline at end of file
diff --git a/src/components/confirmDialog/confirmDialog.component.ts b/src/components/confirmDialog/confirmDialog.component.ts
index b1782761449051a601e8fac5046241e92c572433..5c3d9eb02a50f5847b8ca78a5fabedfd39192f48 100644
--- a/src/components/confirmDialog/confirmDialog.component.ts
+++ b/src/components/confirmDialog/confirmDialog.component.ts
@@ -1,5 +1,5 @@
 import { Component, Inject, Input } from "@angular/core";
-import {MAT_DIALOG_DATA} from "@angular/material/dialog";
+import { MAT_DIALOG_DATA } from "@angular/material/dialog";
 
 @Component({
   selector: 'confirm-dialog-component',
@@ -22,10 +22,14 @@ export class ConfirmDialogComponent {
   @Input()
   public cancelBtnText: string = `Cancel`
 
+  @Input()
+  public markdown: string
+
   constructor(@Inject(MAT_DIALOG_DATA) data: any) {
-    const { title = null, message  = null, okBtnText, cancelBtnText} = data || {}
+    const { title = null, message  = null, markdown, okBtnText, cancelBtnText} = data || {}
     if (title) this.title = title
     if (message) this.message = message
+    if (markdown) this.markdown = markdown
     if (okBtnText) this.okBtnText = okBtnText
     if (cancelBtnText) this.cancelBtnText = cancelBtnText
   }
diff --git a/src/components/confirmDialog/confirmDialog.template.html b/src/components/confirmDialog/confirmDialog.template.html
index df52270e96592f165d5497b5ecb610cfc92bcbcf..401261f1ab1aa9c2324cd8510976e971014db1ea 100644
--- a/src/components/confirmDialog/confirmDialog.template.html
+++ b/src/components/confirmDialog/confirmDialog.template.html
@@ -3,9 +3,16 @@
 </h1>
 
 <mat-dialog-content>
-  <p>
-    {{ message }}
-  </p>
+  <ng-template [ngIf]="markdown" [ngIfElse]="stringMessageTmpl">
+    <markdown-dom [markdown]="markdown">
+    </markdown-dom>
+  </ng-template>
+
+  <ng-template #stringMessageTmpl>
+    <p>
+      {{ message }}
+    </p>
+  </ng-template>
 </mat-dialog-content>
 
 <mat-divider></mat-divider>
diff --git a/src/contextMenuModule/ctxMenuHost.directive.ts b/src/contextMenuModule/ctxMenuHost.directive.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3f1b2b7b8f82a29bdb28147e3cfd652940fa42d8
--- /dev/null
+++ b/src/contextMenuModule/ctxMenuHost.directive.ts
@@ -0,0 +1,31 @@
+import { AfterViewInit, Directive, HostListener, Input, OnDestroy, TemplateRef, ViewContainerRef } from "@angular/core";
+import { ContextMenuService } from "./service";
+import { TContextArg } from "src/viewerModule/viewer.interface";
+
+@Directive({
+  selector: '[ctx-menu-host]'
+})
+
+export class CtxMenuHost implements OnDestroy, AfterViewInit{
+
+  @Input('ctx-menu-host-tmpl')
+  tmplRef: TemplateRef<any>
+
+  @HostListener('contextmenu', ['$event'])
+  onClickListener(ev: MouseEvent){
+    this.svc.showCtxMenu(ev, this.tmplRef)
+  }
+
+  constructor(
+    private vcr: ViewContainerRef,
+    private svc: ContextMenuService<TContextArg<'nehuba' | 'threeSurfer'>>,
+  ){
+  }
+
+  ngAfterViewInit(){
+    this.svc.vcr = this.vcr
+  }
+  ngOnDestroy(){
+    this.svc.vcr = null
+  }
+}
diff --git a/src/contextMenuModule/dismissCtxMenu.directive.ts b/src/contextMenuModule/dismissCtxMenu.directive.ts
new file mode 100644
index 0000000000000000000000000000000000000000..36576fccce8e8a52f6b7c090d5a56e5bf9ce5d1b
--- /dev/null
+++ b/src/contextMenuModule/dismissCtxMenu.directive.ts
@@ -0,0 +1,20 @@
+import { Directive, HostListener } from "@angular/core";
+import { TContextArg } from "src/viewerModule/viewer.interface";
+import { ContextMenuService } from "./service";
+
+@Directive({
+  selector: '[ctx-menu-dismiss]'
+})
+
+export class DismissCtxMenuDirective{
+  @HostListener('click')
+  onClickListener() {
+    this.svc.dismissCtxMenu()
+  }
+
+  constructor(
+    private svc: ContextMenuService<TContextArg<'threeSurfer' | 'nehuba'>>
+  ){
+
+  }
+}
diff --git a/src/contextMenuModule/index.ts b/src/contextMenuModule/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2b7c0908cfc1d7a4a7fa2c891b1d55f87e04f914
--- /dev/null
+++ b/src/contextMenuModule/index.ts
@@ -0,0 +1,3 @@
+export { ContextMenuModule } from './module'
+export { TContextMenuReg, ContextMenuService } from './service'
+export { DismissCtxMenuDirective } from './dismissCtxMenu.directive'
diff --git a/src/contextMenuModule/module.ts b/src/contextMenuModule/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c3529c0b58e9099b46a6733ca3af91905f3d5538
--- /dev/null
+++ b/src/contextMenuModule/module.ts
@@ -0,0 +1,25 @@
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module";
+import { CtxMenuHost } from "./ctxMenuHost.directive";
+import { DismissCtxMenuDirective } from "./dismissCtxMenu.directive";
+
+@NgModule({
+  imports: [
+    AngularMaterialModule,
+    CommonModule,
+  ],
+  declarations: [
+    DismissCtxMenuDirective,
+    CtxMenuHost,
+  ],
+  exports: [
+    DismissCtxMenuDirective,
+    CtxMenuHost,
+  ],
+  providers: [
+    
+  ]
+})
+
+export class ContextMenuModule{}
diff --git a/src/contextMenuModule/service.ts b/src/contextMenuModule/service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..be34051f9cfe3f5cc59a71f9707e62289a8bc8d7
--- /dev/null
+++ b/src/contextMenuModule/service.ts
@@ -0,0 +1,116 @@
+import { Overlay, OverlayRef } from "@angular/cdk/overlay"
+import { TemplatePortal } from "@angular/cdk/portal"
+import { Injectable, TemplateRef, ViewContainerRef } from "@angular/core"
+import { ReplaySubject, Subject, Subscription } from "rxjs"
+import { RegDeregController } from "src/util/regDereg.base"
+
+type TTmpl = {
+  tmpl: TemplateRef<any>
+  data: any
+}
+
+type TSimple = {
+  data: {
+    message: string
+    iconClass?: string
+  }
+}
+
+type TTmplRef = (TTmpl | TSimple) & {
+  order?: number
+  onClick?: Function
+}
+
+type CtxMenuInterArg<T> = {
+  context: T
+  append: (arg: TTmplRef) => void
+}
+
+@Injectable({
+  providedIn: 'root'
+})
+
+export class ContextMenuService<T> extends RegDeregController<CtxMenuInterArg<T>, boolean>{
+  
+  public vcr: ViewContainerRef
+  private overlayRef: OverlayRef
+
+  private subs: Subscription[] = []
+  
+  public context$ = new Subject<T>()
+  public context: T
+
+  public tmplRefs$ = new ReplaySubject<TTmplRef[]>(1)
+  public tmplRefs: TTmplRef[] = []
+
+  constructor(
+    private overlay: Overlay,
+  ){
+    super()
+    this.subs.push(
+      this.context$.subscribe(v => this.context = v)
+    )
+  }
+
+  callRegFns(){
+    let tmplRefs: TTmplRef[] = []
+    for (const fn of this.callbacks){
+      const resp = fn({
+        context: this.context,
+        append: arg => tmplRefs.push(arg),
+      })
+      if (!resp) {
+        tmplRefs = []
+        return false
+      }
+    }
+    this.tmplRefs = tmplRefs
+    this.tmplRefs$.next(tmplRefs)
+    return true
+  }
+
+  dismissCtxMenu(){
+    if (this.overlayRef) {
+      this.overlayRef.dispose()
+      this.overlayRef = null
+    }
+  }
+
+  showCtxMenu(ev: MouseEvent, tmplRef: TemplateRef<any>){
+    if (!this.vcr) {
+      console.warn(`[ctx-menu-host] not attached to any component!`)
+      return
+    }
+    this.dismissCtxMenu()
+    const flag = this.callRegFns()
+    if (!flag) return 
+
+    const { x, y } = ev
+    const positionStrategy = this.overlay.position()
+      .flexibleConnectedTo({ x, y })
+      .withPositions([
+        {
+          originX: 'end',
+          originY: 'bottom',
+          overlayX: 'start',
+          overlayY: 'top',
+        }
+      ])
+
+    this.overlayRef = this.overlay.create({
+      positionStrategy,
+    })
+
+    this.overlayRef.attach(
+      new TemplatePortal(
+        tmplRef,
+        this.vcr,
+        {
+          tmplRefs: this.tmplRefs
+        }
+      )
+    )
+  }
+}
+
+export type TContextMenuReg<T> = (arg: CtxMenuInterArg<T>) => boolean
diff --git a/src/util/directives/dragDrop.directive.ts b/src/dragDropFile/dragDrop.directive.ts
similarity index 93%
rename from src/util/directives/dragDrop.directive.ts
rename to src/dragDropFile/dragDrop.directive.ts
index 4f2689b7bfa41878098fa1c6d87c15c45c20e7be..0427836c4a369ddf3fd7ced09358639d206cdcf1 100644
--- a/src/util/directives/dragDrop.directive.ts
+++ b/src/dragDropFile/dragDrop.directive.ts
@@ -4,15 +4,16 @@ import { debounceTime, map, scan, switchMap } from "rxjs/operators";
 import {MatSnackBar, MatSnackBarRef, SimpleSnackBar} from "@angular/material/snack-bar";
 
 @Directive({
-  selector: '[drag-drop]',
+  selector: '[drag-drop-file]',
+  exportAs: 'dragDropFile'
 })
 
-export class DragDropDirective implements OnInit, OnDestroy {
+export class DragDropFileDirective implements OnInit, OnDestroy {
 
   @Input()
   public snackText: string
 
-  @Output('drag-drop')
+  @Output('drag-drop-file')
   public dragDropOnDrop: EventEmitter<File[]> = new EventEmitter()
 
   @HostBinding('style.transition')
diff --git a/src/dragDropFile/module.ts b/src/dragDropFile/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e3b77d44dc37541f55f62beed2ab213af43c4d77
--- /dev/null
+++ b/src/dragDropFile/module.ts
@@ -0,0 +1,17 @@
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { DragDropFileDirective } from "./dragDrop.directive";
+
+@NgModule({
+  imports: [
+    CommonModule
+  ],
+  declarations: [
+    DragDropFileDirective
+  ],
+  exports: [
+    DragDropFileDirective
+  ]
+})
+
+export class DragDropFileModule{}
\ No newline at end of file
diff --git a/src/getFileInput/fileInputModal/fileInputModal.component.ts b/src/getFileInput/fileInputModal/fileInputModal.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b402c272261e01cf13047a0d9c3c65776ada7850
--- /dev/null
+++ b/src/getFileInput/fileInputModal/fileInputModal.component.ts
@@ -0,0 +1,119 @@
+import { Component, ElementRef, EventEmitter, Inject, Input, Optional, TemplateRef, ViewChild } from "@angular/core";
+import { MAT_DIALOG_DATA } from "@angular/material/dialog";
+import { IFileInputConfig, TFileInputEvent } from "../type";
+
+const FILEINPUT_DEFAULT_LABEL = 'File input'
+
+@Component({
+  selector: 'file-input-modal',
+  templateUrl: './fileInputModal.template.html',
+  styleUrls: [
+    './fileInputModal.style.css'
+  ]
+})
+
+export class FileInputModal implements IFileInputConfig{
+  
+  @Input('file-input-directive-title')
+  title = 'Import'
+
+  @Input('file-input-directive-text')
+  allowText = false
+
+  @Input('file-input-directive-file')
+  allowFile = true
+
+  @Input('file-input-directive-message')
+  messageTmpl: TemplateRef<any>
+
+  @ViewChild('fileInput', { read: ElementRef })
+  private fileInputEl: ElementRef<HTMLInputElement>
+
+  constructor(
+    @Optional() @Inject(MAT_DIALOG_DATA) data: IFileInputConfig
+  ){
+    if (data) {
+      const { allowFile, allowText, messageTmpl, title } = data
+      this.allowFile = allowFile
+      this.allowText = allowText
+      this.messageTmpl = messageTmpl
+      this.title = title || this.title
+    }
+  }
+
+  public hasInput = false
+
+  private _textInput = ''
+  set textInput(val: string) {
+    this._textInput = val
+    this.checkImportable()
+  }
+  get textInput(){
+    return this._textInput
+  }
+
+  public fileInputLabel: string = FILEINPUT_DEFAULT_LABEL
+  public hasFileInput = false
+
+  private _fileInput: File
+  set fileInput(val: File){
+    this._fileInput = val
+    this.hasFileInput = !!this.fileInput
+    this.fileInputLabel = this.hasFileInput
+      ? this._fileInput.name
+      : FILEINPUT_DEFAULT_LABEL
+
+    this.checkImportable()
+  }
+  get fileInput(){
+    return this._fileInput
+  }
+  handleFileInputChange(ev: InputEvent){
+    const target = ev.target as HTMLInputElement
+    this.fileInput = target.files[0]
+  }
+
+  handleFileDrop(files: File[]){
+    this.fileInput = files[0]
+  }
+
+  public importable = false
+  checkImportable(){
+    if (this._textInput.length > 0) {
+      this.importable = true
+      return
+    }
+    if (this.hasFileInput){
+      this.importable = true
+      return
+    }
+    this.importable = false
+  }
+
+  clear(){
+    this.textInput = ''
+    this.fileInput = null
+  }
+
+  public evtEmitter = new EventEmitter<TFileInputEvent<'text' | 'file'>>()
+
+  runImport(){
+    if (this._textInput !== '') {
+      this.evtEmitter.emit({
+        type: 'text',
+        payload: {
+          input: this._textInput
+        }
+      })
+      return
+    }
+    if (this.hasFileInput) {
+      const files = [this.fileInput]
+      this.evtEmitter.emit({
+        type: 'file',
+        payload: { files }
+      })
+      return
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/getFileInput/fileInputModal/fileInputModal.style.css b/src/getFileInput/fileInputModal/fileInputModal.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..3326ccd216c00e08bf82bdf311be32f86cefae46
--- /dev/null
+++ b/src/getFileInput/fileInputModal/fileInputModal.style.css
@@ -0,0 +1,22 @@
+.file-input-label-container
+{
+  box-sizing: border-box;
+  padding: 1rem;
+}
+
+.file-input-label
+{
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  border-radius: 1rem;
+  border: 2px dashed rgba(128,128,128,0.4);
+  opacity: 0.7;
+  transition: opacity 200ms ease-in-out;
+}
+
+.file-input-label:hover
+{
+  cursor: pointer;
+  opacity: 1.0;
+}
\ No newline at end of file
diff --git a/src/getFileInput/fileInputModal/fileInputModal.template.html b/src/getFileInput/fileInputModal/fileInputModal.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..4fdc365d5169260206961acf60c0e117f29ce582
--- /dev/null
+++ b/src/getFileInput/fileInputModal/fileInputModal.template.html
@@ -0,0 +1,55 @@
+<h2 mat-dialog-title>
+  {{ title }}
+</h2>
+<mat-dialog-content>
+  <ng-template [ngIf]="messageTmpl" [ngIfElse]="defaultMessageTmpl" [ngTemplateOutlet]="messageTmpl">
+  </ng-template>
+
+  <ng-template #defaultMessageTmpl>
+    Please select a file to import.
+  </ng-template>
+
+  <div class="w-100 d-flex">
+
+    <!-- text input -->
+    <mat-form-field *ngIf="allowText"
+      class="flex-grow-1 flex-shrink-1 w-0">
+      <mat-label>
+        Text Input
+      </mat-label>
+      <textarea matInput
+        [(ngModel)]="textInput"
+        rows="5"
+        placeholder="Text Input"></textarea>
+    </mat-form-field>
+
+    <!-- file-input -->
+    <div class="file-input-label-container flex-grow-1 flex-shrink-1 w-0 position-relative"
+      (drag-drop-file)="handleFileDrop($event)">
+      <label for="file-input" class="file-input-label w-100 h-100">
+        <i [ngClass]="hasFileInput ? 'fa-file' : 'fa-folder-open'" class="fas"></i>
+        <span class="ml-2">
+          {{ fileInputLabel }}
+        </span>
+      </label>
+      <input (change)="handleFileInputChange($event)"
+        type="file"
+        class="position-absolute left-0 top-0 w-0 h-0 invisible"
+        name="file-input"
+        id="file-input"
+        #fileInput>
+    </div>
+  </div>
+</mat-dialog-content>
+
+<mat-dialog-actions align="end">
+  <button mat-raised-button
+    (click)="runImport()"
+    [disabled]="!importable"
+    color="primary">
+    Import
+  </button>
+  <button mat-button mat-dialog-close>
+    Close
+  </button>
+</mat-dialog-actions>
diff --git a/src/getFileInput/getFileInput.directive.ts b/src/getFileInput/getFileInput.directive.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d1158cdac9de4598957793bec83f2c90761edb2f
--- /dev/null
+++ b/src/getFileInput/getFileInput.directive.ts
@@ -0,0 +1,68 @@
+import { Directive, EventEmitter, HostListener, Input, Output, TemplateRef } from "@angular/core";
+import { MatDialog, MatDialogRef } from "@angular/material/dialog";
+import { FileInputModal } from "./fileInputModal/fileInputModal.component";
+import { IFileInputConfig, TFileInputEvent } from "./type";
+
+@Directive({
+  selector: '[file-input-directive]',
+  exportAs: 'fileInputDirective'
+})
+
+export class FileInputDirective implements IFileInputConfig{
+
+  @Input('file-input-directive-title')
+  title = 'Import'
+
+  @Input('file-input-directive-text')
+  allowText = false
+
+  @Input('file-input-directive-file')
+  allowFile = true
+
+  @Input('file-input-directive-message')
+  messageTmpl: TemplateRef<any>
+
+  @Output('file-input-directive')
+  evtEmitter = new EventEmitter<TFileInputEvent<'text' | 'file'>>()
+
+  private dialogRef: MatDialogRef<FileInputModal>
+
+  @HostListener('click')
+  handleClick(){
+    const { title, allowText, allowFile, messageTmpl } = this
+    this.dialogRef = this.dialog.open(FileInputModal, {
+      width: '65vw',
+      data: {
+        allowText,
+        allowFile,
+        title,
+        messageTmpl,
+      }
+    })
+    const evSub = this.dialogRef.componentInstance.evtEmitter.subscribe(
+      (ev: TFileInputEvent<"text" | "file">) => this.evtEmitter.emit(ev)
+    )
+    this.dialogRef.afterClosed().subscribe(() => {
+      this.dialogRef = null
+      evSub.unsubscribe()
+    })
+  }
+
+  constructor(
+    private dialog: MatDialog
+  ){
+    
+  }
+
+  clear(){
+    if (this.dialogRef) {
+      this.dialogRef.componentInstance.clear()
+    }
+  }
+
+  dismiss(){
+    if (this.dialogRef) {
+      this.dialogRef.close()
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.style.css b/src/getFileInput/index.ts
similarity index 100%
rename from src/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.style.css
rename to src/getFileInput/index.ts
diff --git a/src/getFileInput/module.ts b/src/getFileInput/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..77610abdb95357e856f20d9d59a13ee98ee515ed
--- /dev/null
+++ b/src/getFileInput/module.ts
@@ -0,0 +1,30 @@
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { FileInputDirective } from "./getFileInput.directive";
+import { MatDialogModule } from '@angular/material/dialog';
+import { FileInputModal } from "./fileInputModal/fileInputModal.component";
+import { FormsModule } from "@angular/forms";
+import { MatInputModule } from '@angular/material/input';
+import { MatButtonModule } from '@angular/material/button';
+import { DragDropFileModule } from "src/dragDropFile/module";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    MatDialogModule,
+    FormsModule,
+    MatInputModule,
+    MatButtonModule,
+    DragDropFileModule,
+  ],
+  declarations: [
+    FileInputDirective,
+    FileInputModal,
+  ],
+  exports: [
+    FileInputDirective,
+    FileInputModal,
+  ],
+})
+
+export class FileInputModule{}
\ No newline at end of file
diff --git a/src/getFileInput/type.ts b/src/getFileInput/type.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6b95f06956c8dc9b9dc64798f9a5df1e8d7e3faa
--- /dev/null
+++ b/src/getFileInput/type.ts
@@ -0,0 +1,18 @@
+import { TemplateRef } from "@angular/core";
+
+export interface IFileInputConfig {
+  title: string
+  allowText: boolean
+  allowFile: boolean
+  messageTmpl?: TemplateRef<any>
+}
+
+export type TFileInput = {
+  text: { input: string }
+  file: { files: File[] }
+}
+
+export type TFileInputEvent<Evt extends keyof TFileInput> = {
+  type: Evt
+  payload: TFileInput[Evt]
+}
\ No newline at end of file
diff --git a/src/glue.spec.ts b/src/glue.spec.ts
index aba6f15912e2050d8e2a25bc11d58216bd001934..0124829a8a439ffa3e38920f98d741a450a98bb8 100644
--- a/src/glue.spec.ts
+++ b/src/glue.spec.ts
@@ -1,20 +1,15 @@
 import { TestBed, tick, fakeAsync, discardPeriodicTasks } from "@angular/core/testing"
-import { DatasetPreviewGlue, glueSelectorGetUiStatePreviewingFiles, glueActionRemoveDatasetPreview, datasetPreviewMetaReducer, glueActionAddDatasetPreview, GlueEffects } from "./glue"
+import { DatasetPreviewGlue, glueSelectorGetUiStatePreviewingFiles, glueActionRemoveDatasetPreview, datasetPreviewMetaReducer, glueActionAddDatasetPreview, GlueEffects, ClickInterceptorService } from "./glue"
 import { ACTION_TO_WIDGET_TOKEN, EnumActionToWidget } from "./widget"
 import { provideMockStore, MockStore } from "@ngrx/store/testing"
-import { getRandomHex } from 'common/util'
+import { getRandomHex, getIdObj } from 'common/util'
 import { EnumWidgetTypes, TypeOpenedWidget, uiActionSetPreviewingDatasetFiles, uiStatePreviewingDatasetFilesSelector } from "./services/state/uiState.store.helper"
 import { hot } from "jasmine-marbles"
 import { HttpClientTestingModule, HttpTestingController } from "@angular/common/http/testing"
 import { glueActionToggleDatasetPreview } from './glue'
-import { getIdObj } from 'common/util'
 import { DS_PREVIEW_URL } from 'src/util/constants'
 import { NgLayersService } from "./ui/layerbrowser/ngLayerService.service"
-import { EnumColorMapName } from "./util/colorMaps"
-import { ngViewerSelectorClearView } from "./services/state/ngViewerState/selectors"
-import { tap, ignoreElements } from "rxjs/operators"
-import { merge, of } from "rxjs"
-import { GET_KGDS_PREVIEW_INFO_FROM_ID_FILENAME } from "./ui/databrowserModule/pure"
+import { GET_KGDS_PREVIEW_INFO_FROM_ID_FILENAME } from "./atlasComponents/databrowserModule/pure"
 import { viewerStateSelectedTemplateSelector } from "./services/state/viewerState/selectors"
 import { generalActionError } from "./services/stateStore.helper"
 
@@ -289,42 +284,6 @@ describe('> glue.ts', () => {
         discardPeriodicTasks()
       }))
 
-      it('> on previewing nifti, thresholds, colormap and remove bg flag set properly', fakeAsync(() => {
-        const store = TestBed.inject(MockStore)
-        const ctrl = TestBed.inject(HttpTestingController)
-
-        const layerService = TestBed.inject(NgLayersService)
-
-        const highThresholdMapSpy = spyOn(layerService.highThresholdMap, 'set').and.callThrough()
-        const lowThresholdMapSpy = spyOn(layerService.lowThresholdMap, 'set').and.callThrough()
-        const colorMapMapSpy = spyOn(layerService.colorMapMap, 'set').and.callThrough()
-        const bgFlagSpy = spyOn(layerService.removeBgMap, 'set').and.callThrough()
-
-        const glue = TestBed.inject(DatasetPreviewGlue)
-
-        store.setState({
-          uiState: {
-            previewingDatasetFiles: [ file1 ]
-          }
-        })
-
-        const { datasetId, filename } = file1
-        // debounce at 100ms
-        tick(200)
-
-        const req = ctrl.expectOne(`${DS_PREVIEW_URL}/${encodeURIComponent('minds/core/dataset/v1.0.0')}/${datasetId}/${encodeURIComponent(filename)}`)
-        req.flush(nifti)
-
-        tick(200)
-        const { name, volumeMetadata } = nifti
-        const { min, max } = volumeMetadata
-        expect(highThresholdMapSpy).toHaveBeenCalledWith(name, max)
-        expect(lowThresholdMapSpy).toHaveBeenCalledWith(name, min)
-        expect(colorMapMapSpy).toHaveBeenCalledWith(name, EnumColorMapName.VIRIDIS)
-        expect(bgFlagSpy).toHaveBeenCalledWith(name, true)
-        discardPeriodicTasks()
-      }))
-
       it('> if returns 404, should be handled gracefully', fakeAsync(() => {
 
         const ctrl = TestBed.inject(HttpTestingController)
@@ -510,328 +469,6 @@ describe('> glue.ts', () => {
       
     })
     
-    describe('> selectedRegionPreview$', () => {
-      it('> when one region with origindataset is selected, emits correctly', fakeAsync(() => {
-
-        const store = TestBed.inject(MockStore)
-        const glue = TestBed.inject(DatasetPreviewGlue)
-        const ctrl = TestBed.inject(HttpTestingController)
-        store.overrideSelector(ngViewerSelectorClearView, false)
-        store.setState({
-          ...initialState,
-          viewerState: {
-            regionsSelected: [region1]
-          }
-        })
-        
-        const { kgSchema, kgId, filename } = region1.originDatasets[0]
-        const req = ctrl.expectOne(`${DS_PREVIEW_URL}/${encodeURIComponent(kgSchema)}/${kgId}/${encodeURIComponent(filename)}`)
-        req.flush(nifti)
-        tick(200)
-        expect(glue.selectedRegionPreview$).toBeObservable(
-          hot('a', {
-            a: region1.originDatasets
-          })
-        )
-
-        discardPeriodicTasks()
-      }))
-
-      it('> when regions are selected without originDatasets, emits empty array', () => {
-
-        const store = TestBed.inject(MockStore)
-        const glue = TestBed.inject(DatasetPreviewGlue)
-        store.overrideSelector(ngViewerSelectorClearView, false)
-        store.setState({
-          ...initialState,
-          viewerState: {
-            regionsSelected: [{
-              ...region0,
-              originDatasets: []
-            }, {
-              ...region1,
-              originDatasets: []
-            }]
-          }
-        })
-        
-        expect(glue.selectedRegionPreview$).toBeObservable(
-          hot('a', {
-            a: []
-          })
-        )
-      })
-
-      it('> if multiple region, each with origin datasets are selected, emit array', fakeAsync(() => {
-
-        const store = TestBed.inject(MockStore)
-        const glue = TestBed.inject(DatasetPreviewGlue)
-        const ctrl = TestBed.inject(HttpTestingController)
-        store.overrideSelector(ngViewerSelectorClearView, false)
-        store.setState({
-          ...initialState,
-          viewerState: {
-            regionsSelected: [region0, region1]
-          }
-        })
-        
-        const expectedOriginDatasets = [
-          ...region0.originDatasets,
-          ...region1.originDatasets,
-        ]
-
-        for (const { kgSchema, kgId, filename } of expectedOriginDatasets) {
-          const req = ctrl.expectOne(`${DS_PREVIEW_URL}/${encodeURIComponent(kgSchema)}/${kgId}/${encodeURIComponent(filename)}`)
-          req.flush(nifti)
-        }
-        tick(200)
-        expect(glue.selectedRegionPreview$).toBeObservable(
-          hot('a', {
-            a: expectedOriginDatasets
-          })
-        )
-
-        discardPeriodicTasks()
-      }))
-
-      it('> if regions with multiple originDatasets are selected, emit array containing all origindatasets', fakeAsync(() => {
-
-        const store = TestBed.inject(MockStore)
-        const glue = TestBed.inject(DatasetPreviewGlue)
-        const ctrl = TestBed.inject(HttpTestingController)
-        store.overrideSelector(ngViewerSelectorClearView, false)
-        const originDatasets0 = [
-          ...region0.originDatasets,
-          {
-            kgId: getRandomHex(),
-            kgSchema: 'minds/core/dataset/v1.0.0',
-            filename: getRandomHex()
-          }
-        ]
-        const origindataset1 = [
-          ...region1.originDatasets,
-          {
-            kgSchema: 'minds/core/dataset/v1.0.0',
-            kgId: getRandomHex(),
-            filename: getRandomHex()
-          }
-        ]
-        store.setState({
-          ...initialState,
-          viewerState: {
-            regionsSelected: [{
-              ...region0,
-              originDatasets: originDatasets0
-            }, {
-              ...region1,
-              originDatasets: origindataset1
-            }]
-          }
-        })
-        
-        const expectedOriginDatasets = [
-          ...originDatasets0,
-          ...origindataset1,
-        ]
-
-        for (const { kgSchema, kgId, filename } of expectedOriginDatasets) {
-          const req = ctrl.expectOne(`${DS_PREVIEW_URL}/${encodeURIComponent(kgSchema)}/${kgId}/${encodeURIComponent(filename)}`)
-          req.flush(nifti)
-        }
-        tick(200)
-        expect(glue.selectedRegionPreview$).toBeObservable(
-          hot('a', {
-            a: expectedOriginDatasets
-          })
-        )
-        discardPeriodicTasks()
-      }))
-    })
-
-    describe('> onRegionSelectChangeShowPreview$', () => {
-      it('> calls getDatasetPreviewFromId for each of the selectedRegion', fakeAsync(() => {
-
-        /**
-         * Testing Store observable 
-         * https://stackoverflow.com/a/61871144/6059235
-         */
-        const store = TestBed.inject(MockStore)
-        const glue = TestBed.inject(DatasetPreviewGlue)
-        const ctrl = TestBed.inject(HttpTestingController)
-        store.overrideSelector(ngViewerSelectorClearView, false)
-
-        const getDatasetPreviewFromIdSpy = spyOn(glue, 'getDatasetPreviewFromId').and.callThrough()
-        store.setState({
-          ...initialState,
-          viewerState: {
-            regionsSelected: [region1]
-          }
-        })
-        
-        const { kgSchema, kgId, filename } = region1.originDatasets[0]
-        const req = ctrl.expectOne(`${DS_PREVIEW_URL}/${encodeURIComponent(kgSchema)}/${kgId}/${encodeURIComponent(filename)}`)
-        req.flush(nifti)
-        tick(200)
-
-        for (const { kgId, kgSchema, filename } of region1.originDatasets) {
-          expect(getDatasetPreviewFromIdSpy).toHaveBeenCalledWith({
-            datasetId: kgId,
-            datasetSchema: kgSchema,
-            filename
-          })
-        }
-
-        expect(glue.onRegionSelectChangeShowPreview$).toBeObservable(
-          hot('a', {
-            a: [ {
-              ...nifti,
-              filename: region1.originDatasets[0].filename,
-              datasetId: region1.originDatasets[0].kgId,
-              datasetSchema: kgSchema,
-            } ]
-          })
-        )
-
-        discardPeriodicTasks()
-      }))
-    })
-
-    describe('> onRegionDeselectRemovePreview$', () => {
-      it('> on region selected [ region ] > [], emits', fakeAsync(() => {
-
-        const store = TestBed.inject(MockStore)
-        store.overrideSelector(ngViewerSelectorClearView, false)
-        const glue = TestBed.inject(DatasetPreviewGlue)
-
-        const regionsSelected$ = hot('bab', {
-          a: [region1],
-          b: []
-        })
-
-        const spy = spyOn(glue, 'getDatasetPreviewFromId')
-        spy.and.returnValue(of({
-          ...nifti,
-          filename: region1.originDatasets[0].filename,
-          datasetId: region1.originDatasets[0].kgId,
-        }))
-
-        const src$ = merge(
-          regionsSelected$.pipe(
-            tap(regionsSelected => store.setState({
-              ...initialState,
-              viewerState: {
-                regionsSelected
-              }
-            })),
-            ignoreElements()
-          ),
-          glue.onRegionDeselectRemovePreview$
-        )
-
-        src$.subscribe()
-
-        expect(glue.onRegionDeselectRemovePreview$).toBeObservable(
-          hot('bba', {
-            a: [{
-              ...nifti,
-              filename: region1.originDatasets[0].filename,
-              datasetId: region1.originDatasets[0].kgId,
-            }],
-            b: []
-          })
-        )
-
-        tick(200)
-        discardPeriodicTasks()
-      }))
-    })
-
-    describe('> onClearviewRemovePreview$', () => {
-      it('> on regions selected [ region ] > clear view selector returns true, emits ', fakeAsync(() => {
-        const store = TestBed.inject(MockStore)
-        store.overrideSelector(ngViewerSelectorClearView, true)
-
-        const glue = TestBed.inject(DatasetPreviewGlue)
-
-        const spy = spyOn(glue, 'getDatasetPreviewFromId')
-        spy.and.returnValue(of({
-          ...nifti,
-          filename: region1.originDatasets[0].filename,
-          datasetId: region1.originDatasets[0].kgId,
-        }))
-
-        store.setState({
-          ...initialState,
-          viewerState: {
-            regionsSelected: [region1]
-          }
-        })
-
-        expect(glue.onClearviewRemovePreview$).toBeObservable(
-          hot('a', {
-            a: [{
-              ...nifti,
-              filename: region1.originDatasets[0].filename,
-              datasetId: region1.originDatasets[0].kgId,
-            }],
-            b: []
-          })
-        )
-
-        tick(200)
-        discardPeriodicTasks()
-      }))
-    })
-
-    describe('> onClearviewAddPreview$', () => {
-      it('> on region selected [ region ] > clear view selector returns false, emits', fakeAsync(() => {
-        const store = TestBed.inject(MockStore)
-        const overridenSelector = store.overrideSelector(ngViewerSelectorClearView, true)
-
-        /**
-         * skips first false
-         */
-        const overridenSelector$ = hot('bab', {
-          a: true,
-          b: false
-        })
-
-        const glue = TestBed.inject(DatasetPreviewGlue)
-
-        const spy = spyOn(glue, 'getDatasetPreviewFromId')
-        spy.and.returnValue(of({
-          ...nifti,
-          filename: region1.originDatasets[0].filename,
-          datasetId: region1.originDatasets[0].kgId,
-        }))
-
-        store.setState({
-          ...initialState,
-          viewerState: {
-            regionsSelected: [region1]
-          }
-        })
-
-        overridenSelector$.subscribe(flag => {
-          overridenSelector.setResult(flag)
-          store.refreshState()
-        })
-
-        expect(glue.onClearviewAddPreview$).toBeObservable(
-          hot('--a', {
-            a: [{
-              ...nifti,
-              filename: region1.originDatasets[0].filename,
-              datasetId: region1.originDatasets[0].kgId,
-            }],
-            b: []
-          })
-        )
-
-        tick(200)
-        discardPeriodicTasks()
-      }))
-    })
   })
 
 
@@ -1211,65 +848,51 @@ describe('> glue.ts', () => {
     /**
      * TODO finish writing the test for ClickInterceptorService
      */
+    let interceptorService: ClickInterceptorService
 
-    it('can obtain override fn', () => {
-
+    beforeEach(() => {
+      interceptorService = new ClickInterceptorService()
     })
 
-    describe('> if getUserToSelectRegion.length === 0', () => {
-
-      it('by default, next fn will be called', () => {
-
-      })
-  
-      it('if apiService.getUserToSelectRegion.length === 0, and mouseoversegment.length > 0 calls next', () => {
-
-      })
-    })
-    describe('> if getUserToSelectRegion.length > 0', () => {
-      it('if both apiService.getUserToSelectRegion.length > 0 and mouseoverSegment.length >0, then next will not be called, but rs will be', () => {
-        
+    describe('> # callRegFns', () => {
+      let spy1: jasmine.Spy,
+        spy2: jasmine.Spy,
+        spy3: jasmine.Spy
+      beforeEach(() => {
+        spy1 = jasmine.createSpy('spy1')
+        spy2 = jasmine.createSpy('spy2')
+        spy3 = jasmine.createSpy('spy3')
+        interceptorService['callbacks'] = [
+          spy1,
+          spy2,
+          spy3,
+        ]
+        spy1.and.returnValue(true)
+        spy2.and.returnValue(true)
+        spy3.and.returnValue(true)
       })
-      it('if multiple getUserToSelectRegion handler exists, it resolves in a LIFO manner', () => {
+      it('> fns are all called', () => {
 
+        interceptorService.callRegFns('stuff')
+        expect(spy1).toHaveBeenCalled()
+        expect(spy2).toHaveBeenCalled()
+        expect(spy3).toHaveBeenCalled()
       })
+      it('> will run fns from first idx to last idx', () => {
 
-      describe('> if spec is not set (defaults to parcellation region mode)', () => {
-
-        it('if apiService.getUserToSelectRegion.length > 0, but mouseoversegment.length ===0, will not call next, will not rs, will not call rj', () => {
-
-        })
+        interceptorService.callRegFns('stuff')
+        expect(spy1).toHaveBeenCalledBefore(spy2)
+        expect(spy2).toHaveBeenCalledBefore(spy3)
       })
+      it('> will stop at when next is not called', () => {
 
-      describe('> if spec is set', () => {
-        describe('> if spec is set to PARCELLATION_REGION', () => {
-
-          it('> mouseoversegment.length === 0, will not call next, will not rs, will not call rj', () => {
-
-          })
-          
-          it('> mouseoversegment.length > 0, will not call next, will call rs', () => {
-
-          })
-        })
+        spy2.and.returnValue(false)
+        interceptorService.callRegFns('stuff')
 
-        describe('> if spec is set to POINT', () => {
-          it('> rs is called if mouseoversegment.length === 0', () => {
-
-          })
-          it('> rs is called with correct arg if mouseoversegment.length > 0', () => {
-
-          })
-        })
-
-        describe('> if multiple getUserToSelectRegion exist', () => {
-          it('> only the last Promise will be evaluated', () => {
-
-      
-          })
-        })
+        expect(spy1).toHaveBeenCalled()
+        expect(spy2).toHaveBeenCalled()
+        expect(spy3).not.toHaveBeenCalled()
       })
     })
-
   })
 })
diff --git a/src/glue.ts b/src/glue.ts
index 1eae95c5a1089c483e3d5ecdec672e2f5814e211..ea07500c18b3839ba05e9d637c1387c48a370d03 100644
--- a/src/glue.ts
+++ b/src/glue.ts
@@ -1,31 +1,28 @@
-import { uiActionSetPreviewingDatasetFiles, IDatasetPreviewData, uiStateShowBottomSheet, uiStatePreviewingDatasetFilesSelector } from "./services/state/uiState.store.helper"
+import { uiActionSetPreviewingDatasetFiles, IDatasetPreviewData, uiStatePreviewingDatasetFilesSelector } from "./services/state/uiState.store.helper"
 import { OnDestroy, Injectable, Optional, Inject, InjectionToken } from "@angular/core"
-import { PreviewComponentWrapper, DatasetPreview, determinePreviewFileType, EnumPreviewFileTypes, IKgDataEntry, getKgSchemaIdFromFullId, GET_KGDS_PREVIEW_INFO_FROM_ID_FILENAME } from "./ui/databrowserModule/pure"
+import { PreviewComponentWrapper, DatasetPreview, determinePreviewFileType, EnumPreviewFileTypes, IKgDataEntry, getKgSchemaIdFromFullId, GET_KGDS_PREVIEW_INFO_FROM_ID_FILENAME } from "./atlasComponents/databrowserModule/pure"
 import { Subscription, Observable, forkJoin, of, merge, combineLatest } from "rxjs"
 import { select, Store, ActionReducer, createAction, props, createSelector, Action } from "@ngrx/store"
-import { startWith, map, shareReplay, pairwise, debounceTime, distinctUntilChanged, tap, switchMap, withLatestFrom, mapTo, switchMapTo, filter, skip, catchError, bufferTime } from "rxjs/operators"
+import { startWith, map, shareReplay, pairwise, debounceTime, distinctUntilChanged, tap, switchMap, withLatestFrom, mapTo, switchMapTo, filter, skip, catchError } from "rxjs/operators"
 import { TypeActionToWidget, EnumActionToWidget, ACTION_TO_WIDGET_TOKEN } from "./widget"
 import { getIdObj } from 'common/util'
 import { MatDialogRef } from "@angular/material/dialog"
 import { HttpClient } from "@angular/common/http"
-import { DS_PREVIEW_URL, getShader, PMAP_DEFAULT_CONFIG } from 'src/util/constants'
-import { ngViewerActionAddNgLayer, ngViewerActionRemoveNgLayer, INgLayerInterface } from "./services/state/ngViewerState.store.helper"
+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 { EnumColorMapName } from "./util/colorMaps"
 import { Effect } from "@ngrx/effects"
 import { viewerStateSelectedRegionsSelector, viewerStateSelectedTemplateSelector, viewerStateSelectedParcellationSelector } from "./services/state/viewerState/selectors"
-import { ngViewerSelectorClearView } from "./services/state/ngViewerState/selectors"
 import { ngViewerActionClearView } from './services/state/ngViewerState/actions'
 import { generalActionError } from "./services/stateStore.helper"
+import { RegDeregController } from "./util/regDereg.base"
 
 const PREVIEW_FILE_TYPES_NO_UI = [
   EnumPreviewFileTypes.NIFTI,
   EnumPreviewFileTypes.VOLUMES
 ]
 
-const DATASET_PREVIEW_ANNOTATION = `DATASET_PREVIEW_ANNOTATION`
-
 const prvFilterNull = ({ prvToDismiss, prvToShow }) => ({
   prvToDismiss: prvToDismiss.filter(v => !!v),
   prvToShow: prvToShow.filter(v => !!v),
@@ -295,57 +292,6 @@ export class DatasetPreviewGlue implements IDatasetPreviewGlue, OnDestroy{
     )
   }
 
-  public selectedRegionPreview$ = this.store$.pipe(
-    select(state => state?.viewerState?.regionsSelected),
-    filter(regions => !!regions),
-    map(regions => /** effectively flatMap */ regions.reduce((acc, curr) => acc.concat(
-      curr.originDatasets && Array.isArray(curr.originDatasets) && curr.originDatasets.length > 0
-        ? curr.originDatasets
-        : []
-    ), [])),
-  )
-
-  public onRegionSelectChangeShowPreview$ = this.selectedRegionPreview$.pipe(
-    switchMap(arr => arr.length > 0
-      ? forkJoin(arr.map(({ kgId, kgSchema, filename }) => this.getDatasetPreviewFromId({ datasetId: kgId, datasetSchema: kgSchema, filename })))
-      : of([])
-    ),
-    map(arr => arr.filter(item => !!item)),
-    shareReplay(1),
-  )
-
-  public onRegionDeselectRemovePreview$ = this.onRegionSelectChangeShowPreview$.pipe(
-    pairwise(),
-    map(([oArr, nArr]) => oArr.filter((item: any) => {
-      return !nArr
-        .map(DatasetPreviewGlue.GetDatasetPreviewId)
-        .includes(
-          DatasetPreviewGlue.GetDatasetPreviewId(item)
-        )
-    })),
-  )
-
-  public onClearviewRemovePreview$ = this.onRegionSelectChangeShowPreview$.pipe(
-    filter(arr => arr.length > 0),
-    switchMap(arr => this.store$.pipe(
-      select(ngViewerSelectorClearView),
-      distinctUntilChanged(),
-      filter(val => val),
-      mapTo(arr)
-    )),
-  )
-
-  public onClearviewAddPreview$ = this.onRegionSelectChangeShowPreview$.pipe(
-    filter(arr => arr.length > 0),
-    switchMap(arr => this.store$.pipe(
-      select(ngViewerSelectorClearView),
-      distinctUntilChanged(),
-      filter(val => !val),
-      skip(1),
-      mapTo(arr)
-    ))
-  )
-
   private fetchedDatasetPreviewCache: Map<string, Observable<any>> = new Map()
   public getDatasetPreviewFromId({ datasetSchema = 'minds/core/dataset/v1.0.0', datasetId, filename }: IDatasetPreviewData){
     const dsPrvId = DatasetPreviewGlue.GetDatasetPreviewId({ datasetSchema, datasetId, filename })
@@ -422,154 +368,6 @@ export class DatasetPreviewGlue implements IDatasetPreviewGlue, OnDestroy{
         }
       })
     )
-
-    // managing niftiVolumes
-    // monitors previewDatasetFile obs to add/remove ng layer
-
-    this.subscriptions.push(
-      merge(
-        this.getDiffDatasetFilesPreviews(
-          dsPrv => determinePreviewFileType(dsPrv) === EnumPreviewFileTypes.NIFTI
-        ),
-        this.onRegionSelectChangeShowPreview$.pipe(
-          map(prvToShow => ({ prvToShow, prvToDismiss: [] }))
-        ),
-        this.onRegionDeselectRemovePreview$.pipe(
-          map(prvToDismiss => ({ prvToShow: [], prvToDismiss }))
-        ),
-        this.onClearviewRemovePreview$.pipe(
-          map(prvToDismiss => ({ prvToDismiss, prvToShow: [] }))
-        ),
-        this.onClearviewAddPreview$.pipe(
-          map(prvToShow => ({ prvToDismiss: [], prvToShow }))
-        )
-      ).pipe(
-        map(prvFilterNull),
-        bufferTime(15),
-        map(arr => {
-          const prvToDismiss = []
-          const prvToShow = []
-
-          const showPrvIds = new Set()
-          const dismissPrvIds = new Set()
-
-          for (const { prvToDismiss: dismisses, prvToShow: shows } of arr) {
-            for (const dismiss of dismisses) {
-
-              const id = DatasetPreviewGlue.GetDatasetPreviewId(dismiss)
-              if (!dismissPrvIds.has(id)) {
-                dismissPrvIds.add(id)
-                prvToDismiss.push(dismiss)
-              }
-            }
-
-            for (const show of shows) {
-              const id = DatasetPreviewGlue.GetDatasetPreviewId(show)
-              if (!dismissPrvIds.has(id) && !showPrvIds.has(id)) {
-                showPrvIds.add(id)
-                prvToShow.push(show)
-              }
-            }
-          }
-
-          return {
-            prvToDismiss,
-            prvToShow
-          }
-        }),
-        withLatestFrom(this.store$.pipe(
-          select(state => state?.viewerState?.templateSelected || null),
-          distinctUntilChanged(),
-        ))
-      ).subscribe(([ { prvToShow, prvToDismiss }, templateSelected ]) => {
-        // TODO consider where to check validity of previewed nifti file
-        for (const prv of prvToShow) {
-
-          const { url, filename, name, volumeMetadata = {} } = prv
-          const { min, max, colormap = EnumColorMapName.VIRIDIS } = volumeMetadata || {}
-          
-          const previewFileId = DatasetPreviewGlue.GetDatasetPreviewId(prv)
-
-          const shaderObj = {
-            ...PMAP_DEFAULT_CONFIG,
-            ...{ colormap },
-            ...( typeof min !== 'undefined' ? { lowThreshold: min } : {} ),
-            ...( max ? { highThreshold: max } : { highThreshold: 1 } )
-          }
-
-          const layer = {
-            // name: filename,
-            name: name || filename,
-            id: previewFileId,
-            source : `nifti://${url}`,
-            mixability : 'nonmixable',
-            shader : getShader(shaderObj),
-            annotation: `${DATASET_PREVIEW_ANNOTATION} ${filename}`
-          }
-
-          const { name: layerName } = layer
-          const { colormap: cmap, lowThreshold, highThreshold, removeBg } = shaderObj
-
-          this.layersService.highThresholdMap.set(layerName, highThreshold)
-          this.layersService.lowThresholdMap.set(layerName, lowThreshold)
-          this.layersService.colorMapMap.set(layerName, cmap)
-          this.layersService.removeBgMap.set(layerName, removeBg)
-
-          this.store$.dispatch(
-            ngViewerActionAddNgLayer({ layer })
-          )
-        }
-
-        for (const prv of prvToDismiss) {
-          const { url, filename, name } = prv
-          const previewFileId = DatasetPreviewGlue.GetDatasetPreviewId(prv)
-          const layer = {
-            name: name || filename,
-            id: previewFileId,
-            source : `nifti://${url}`,
-            mixability : 'nonmixable',
-            shader : getShader(PMAP_DEFAULT_CONFIG),
-            annotation: `${DATASET_PREVIEW_ANNOTATION} ${filename}`
-          }
-          this.store$.dispatch(
-            ngViewerActionRemoveNgLayer({ layer })
-          )
-        }
-
-        if (prvToShow.length > 0) this.store$.dispatch(uiStateShowBottomSheet({ bottomSheetTemplate: null }))
-      })
-    )
-
-    // monitors ngViewerStateLayers, and if user removes, also remove dataset preview, if exists
-    this.subscriptions.push(
-      this.store$.pipe(
-        select(state => state?.ngViewerState?.layers || []),
-        distinctUntilChanged(),
-        pairwise(),
-        map(([o, n]: [INgLayerInterface[], INgLayerInterface[]]) => {
-          const nNameSet = new Set(n.map(({ name }) => name))
-          const oNameSet = new Set(o.map(({ name }) => name))
-          return {
-            add: n.filter(({ name: nName }) => !oNameSet.has(nName)),
-            remove: o.filter(({ name: oName }) => !nNameSet.has(oName)),
-          }
-        }),
-        map(({ remove }) => remove),
-      ).subscribe(layers => {
-        for (const layer of layers) {
-          const { id } = layer
-          if (!id) return console.warn(`monitoring ngViewerStateLayers id is undefined`)
-          try {
-            const { datasetId, filename } = DatasetPreviewGlue.GetDatasetPreviewFromId(layer.id)
-            this.store$.dispatch(
-              glueActionRemoveDatasetPreview({ datasetPreviewFile: { filename, datasetId } })
-            )
-          } catch (e) {
-            console.warn(`monitoring ngViewerStateLayers parsing id or dispatching action failed`, e)
-          }
-        }
-      })
-    )
   }
 
   private closeDatasetPreviewWidget(data: IDatasetPreviewData){
@@ -590,7 +388,6 @@ export class DatasetPreviewGlue implements IDatasetPreviewGlue, OnDestroy{
   }
 
   private openDatasetPreviewWidget(data: IDatasetPreviewData) {
-    console.log({ data })
     const { datasetId: kgId, filename } = data
 
     if (!!this.actionOnWidget) {
@@ -726,34 +523,23 @@ export const gluActionSetFavDataset = createAction(
   providedIn: 'root'
 })
 
-export class ClickInterceptorService{
-  private clickInterceptorStack: Function[] = []
-
-  removeInterceptor(fn: Function) {
-    const idx = this.clickInterceptorStack.findIndex(int => int === fn)
-    if (idx < 0) {
-      console.warn(`clickInterceptorService could not remove the function. Did you pass the EXACT reference? 
-      Anonymouse functions such as () => {}  or .bind will create a new reference! 
-      You may want to assign .bind to a ref, and pass it to register and unregister functions`)
-    } else {
-      this.clickInterceptorStack.splice(idx, 1)
-    }
-  }
-  addInterceptor(fn: Function, atTheEnd?: boolean) {
-    if (atTheEnd) {
-      this.clickInterceptorStack.push(fn)
-    } else {
-      this.clickInterceptorStack.unshift(fn)
+export class ClickInterceptorService extends RegDeregController<any, boolean>{
+
+  callRegFns(ev: any){
+    let intercepted = false
+    for (const clickInc of this.callbacks) {
+      
+      const runNext = clickInc(ev)
+      if (!runNext) {
+        intercepted = true
+        break
+      }
     }
+
+    if (!intercepted) this.fallback(ev)
   }
 
-  run(ev: any){
-    for (const clickInc of this.clickInterceptorStack) {
-      let runNext = false
-      clickInc(ev, () => {
-        runNext = true
-      })
-      if (!runNext) break
-    }
+  fallback(_ev: any) {
+    // called when the call has not been intercepted
   }
 }
diff --git a/src/index.html b/src/index.html
index 8b800ab83f784074bd34125e3bf21a7841258f95..f7c2ab2955adf509da76f7df12a95dc993158231 100644
--- a/src/index.html
+++ b/src/index.html
@@ -11,9 +11,11 @@
   <link rel="stylesheet" href="icons/iav-icons.css">
   <link rel="stylesheet" href="theme.css">
   <link rel="stylesheet" href="version.css">
+  <link rel="icon" type="image/png" href="res/favicons/favicon-128-light.png"/>
   
-  <script src="https://unpkg.com/kg-dataset-previewer@1.1.5/dist/kg-dataset-previewer/kg-dataset-previewer.js" defer>
+  <script src="https://unpkg.com/kg-dataset-previewer@1.2.0/dist/kg-dataset-previewer/kg-dataset-previewer.js" defer>
   </script>
+  <script src="https://unpkg.com/three-surfer@0.0.10/dist/bundle.js" defer></script>
 
   <title>Interactive Atlas Viewer</title>
   <script type="application/ld+json">
diff --git a/src/ui/config/currentLayout/currentLayout.component.ts b/src/layouts/currentLayout/currentLayout.component.ts
similarity index 100%
rename from src/ui/config/currentLayout/currentLayout.component.ts
rename to src/layouts/currentLayout/currentLayout.component.ts
diff --git a/src/layouts/currentLayout/currentLayout.style.css b/src/layouts/currentLayout/currentLayout.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/ui/config/currentLayout/currentLayout.template.html b/src/layouts/currentLayout/currentLayout.template.html
similarity index 100%
rename from src/ui/config/currentLayout/currentLayout.template.html
rename to src/layouts/currentLayout/currentLayout.template.html
diff --git a/src/layouts/fourCorners/fourCorners.component.ts b/src/layouts/fourCorners/fourCorners.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6bae516c960e2c6b3e7344b8bb46274a310d1c95
--- /dev/null
+++ b/src/layouts/fourCorners/fourCorners.component.ts
@@ -0,0 +1,14 @@
+import { Component, Input } from "@angular/core";
+
+@Component({
+  selector: 'iav-layout-fourcorners',
+  templateUrl: './fourCorners.template.html',
+  styleUrls: [
+    './fourCorners.style.css'
+  ]
+})
+
+export class FourCornersCmp{
+  @Input('iav-layout-fourcorners-cnr-cntr-ngclass')
+  cornerContainerClasses = {}
+}
diff --git a/src/layouts/fourCorners/fourCorners.style.css b/src/layouts/fourCorners/fourCorners.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..352b490b2acf42d4d92344bdc0be28d4f8cf80da
--- /dev/null
+++ b/src/layouts/fourCorners/fourCorners.style.css
@@ -0,0 +1,13 @@
+:host
+{
+  display: block;
+  width: 100%;
+  height: 100%;
+
+  position: relative;
+}
+
+.corner-container
+{
+  z-index: 5;
+}
\ No newline at end of file
diff --git a/src/layouts/fourCorners/fourCorners.template.html b/src/layouts/fourCorners/fourCorners.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..96cfe86cdb46e296f4b44fa92adfb377dec010fd
--- /dev/null
+++ b/src/layouts/fourCorners/fourCorners.template.html
@@ -0,0 +1,20 @@
+<div class="position-absolute top-0 left-0 w-100 h-100">
+  <ng-content select="[iavLayoutFourCornersContent]"></ng-content>
+</div>
+
+<div [ngClass]="cornerContainerClasses"
+  class="corner-container position-absolute top-0 left-0">
+  <ng-content select="[iavLayoutFourCornersTopLeft]"></ng-content>  
+</div>
+<div [ngClass]="cornerContainerClasses"
+  class="corner-container position-absolute top-0 right-0">
+  <ng-content select="[iavLayoutFourCornersTopRight]"></ng-content>  
+</div>
+<div [ngClass]="cornerContainerClasses"
+  class="corner-container position-absolute bottom-0 left-0">
+  <ng-content select="[iavLayoutFourCornersBottomLeft]"></ng-content>  
+</div>
+<div [ngClass]="cornerContainerClasses"
+  class="corner-container position-absolute bottom-0 right-0">
+  <ng-content select="[iavLayoutFourCornersBottomRight]"></ng-content>  
+</div>
\ No newline at end of file
diff --git a/src/layouts/layout.module.ts b/src/layouts/layout.module.ts
index b10a09b6bb519316c56a22550bc445afc5c5875b..0923ccb50102bfcde21c45a8f8bafe250ba31863 100644
--- a/src/layouts/layout.module.ts
+++ b/src/layouts/layout.module.ts
@@ -2,9 +2,13 @@ import { NgModule } from "@angular/core";
 import { BrowserModule } from "@angular/platform-browser";
 import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
 import { ComponentsModule } from "../components/components.module";
+import { CurrentLayout } from "./currentLayout/currentLayout.component";
 import { FloatingLayoutContainer } from "./floating/floating.component";
-import { LayoutsExample } from "./layoutsExample/layoutsExample.component";
-import { LayoutMainSide } from "./mainside/mainside.component";
+import { FourCornersCmp } from "./fourCorners/fourCorners.component";
+import { FourPanelLayout } from "./layouts/fourPanel/fourPanel.component";
+import { HorizontalOneThree } from "./layouts/h13/h13.component";
+import { SinglePanel } from "./layouts/single/single.component";
+import { VerticalOneThree } from "./layouts/v13/v13.component";
 
 @NgModule({
   imports : [
@@ -13,17 +17,24 @@ import { LayoutMainSide } from "./mainside/mainside.component";
     ComponentsModule,
   ],
   declarations : [
-    LayoutMainSide,
     FloatingLayoutContainer,
+    FourCornersCmp,
+    CurrentLayout,
 
-    LayoutsExample,
+    FourPanelLayout,
+    HorizontalOneThree,
+    SinglePanel,
+    VerticalOneThree,
   ],
   exports : [
     BrowserAnimationsModule,
-    LayoutMainSide,
     FloatingLayoutContainer,
-
-    LayoutsExample,
+    FourCornersCmp,
+    CurrentLayout,
+    FourPanelLayout,
+    HorizontalOneThree,
+    SinglePanel,
+    VerticalOneThree,
   ],
 })
 
diff --git a/src/ui/config/layouts/fourPanel/fourPanel.component.ts b/src/layouts/layouts/fourPanel/fourPanel.component.ts
similarity index 100%
rename from src/ui/config/layouts/fourPanel/fourPanel.component.ts
rename to src/layouts/layouts/fourPanel/fourPanel.component.ts
diff --git a/src/ui/config/layouts/fourPanel/fourPanel.style.css b/src/layouts/layouts/fourPanel/fourPanel.style.css
similarity index 100%
rename from src/ui/config/layouts/fourPanel/fourPanel.style.css
rename to src/layouts/layouts/fourPanel/fourPanel.style.css
diff --git a/src/ui/config/layouts/fourPanel/fourPanel.template.html b/src/layouts/layouts/fourPanel/fourPanel.template.html
similarity index 100%
rename from src/ui/config/layouts/fourPanel/fourPanel.template.html
rename to src/layouts/layouts/fourPanel/fourPanel.template.html
diff --git a/src/ui/config/layouts/h13/h13.component.ts b/src/layouts/layouts/h13/h13.component.ts
similarity index 100%
rename from src/ui/config/layouts/h13/h13.component.ts
rename to src/layouts/layouts/h13/h13.component.ts
diff --git a/src/ui/config/layouts/h13/h13.style.css b/src/layouts/layouts/h13/h13.style.css
similarity index 100%
rename from src/ui/config/layouts/h13/h13.style.css
rename to src/layouts/layouts/h13/h13.style.css
diff --git a/src/ui/config/layouts/h13/h13.template.html b/src/layouts/layouts/h13/h13.template.html
similarity index 100%
rename from src/ui/config/layouts/h13/h13.template.html
rename to src/layouts/layouts/h13/h13.template.html
diff --git a/src/ui/config/layouts/single/single.component.ts b/src/layouts/layouts/single/single.component.ts
similarity index 100%
rename from src/ui/config/layouts/single/single.component.ts
rename to src/layouts/layouts/single/single.component.ts
diff --git a/src/ui/config/layouts/single/single.style.css b/src/layouts/layouts/single/single.style.css
similarity index 100%
rename from src/ui/config/layouts/single/single.style.css
rename to src/layouts/layouts/single/single.style.css
diff --git a/src/ui/config/layouts/single/single.template.html b/src/layouts/layouts/single/single.template.html
similarity index 100%
rename from src/ui/config/layouts/single/single.template.html
rename to src/layouts/layouts/single/single.template.html
diff --git a/src/ui/config/layouts/v13/v13.component.ts b/src/layouts/layouts/v13/v13.component.ts
similarity index 100%
rename from src/ui/config/layouts/v13/v13.component.ts
rename to src/layouts/layouts/v13/v13.component.ts
diff --git a/src/ui/config/layouts/v13/v13.style.css b/src/layouts/layouts/v13/v13.style.css
similarity index 100%
rename from src/ui/config/layouts/v13/v13.style.css
rename to src/layouts/layouts/v13/v13.style.css
diff --git a/src/ui/config/layouts/v13/v13.template.html b/src/layouts/layouts/v13/v13.template.html
similarity index 100%
rename from src/ui/config/layouts/v13/v13.template.html
rename to src/layouts/layouts/v13/v13.template.html
diff --git a/src/layouts/layoutsExample/layoutsExample.component.ts b/src/layouts/layoutsExample/layoutsExample.component.ts
deleted file mode 100644
index 4cc69683628ef3314e0145e598d70f2556aae570..0000000000000000000000000000000000000000
--- a/src/layouts/layoutsExample/layoutsExample.component.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Component } from "@angular/core";
-
-@Component({
-  selector : 'layouts-example',
-  templateUrl : './layoutsExample.template.html',
-  styleUrls : [
-    `./layoutsExample.style.css`,
-  ],
-})
-
-export class LayoutsExample {
-  public mainsideOverlay: boolean = true
-  public mainsideShowSide: boolean = true
-  public mainsideSideWidth: number = 100
-}
diff --git a/src/layouts/layoutsExample/layoutsExample.style.css b/src/layouts/layoutsExample/layoutsExample.style.css
deleted file mode 100644
index 820e6ef8000438835f0570e983fe7315024f8e43..0000000000000000000000000000000000000000
--- a/src/layouts/layoutsExample/layoutsExample.style.css
+++ /dev/null
@@ -1,9 +0,0 @@
-div[maincontent]
-{
-  background-color:rgba(200,128,128,0.2);
-}
-
-div[sidecontent]
-{
-  background-color:rgba(128,200,128,0.2);
-}
\ No newline at end of file
diff --git a/src/layouts/layoutsExample/layoutsExample.template.html b/src/layouts/layoutsExample/layoutsExample.template.html
deleted file mode 100644
index 479867efe8514eb385edd2d1d46e5420d6d31d76..0000000000000000000000000000000000000000
--- a/src/layouts/layoutsExample/layoutsExample.template.html
+++ /dev/null
@@ -1,39 +0,0 @@
-<div class = "container-fluid">
-  <div class = "row">
-    <div class = "col-md-4">
-      <div 
-        (click) = "mainsideOverlay = !mainsideOverlay"
-        [ngClass] = "mainsideOverlay ? 'btn-primary' : 'btn-default'" 
-        class = "btn">
-        overlay : {{ mainsideOverlay ? 'true' : 'false' }}
-      </div>
-      <div 
-        (click) = "mainsideShowSide = !mainsideShowSide"
-        [ngClass] = "mainsideShowSide ? 'btn-primary' : 'btn-default'" 
-        class = "btn">
-        showside : {{ mainsideShowSide ? 'true' : 'false' }}
-      </div>
-    </div>
-    <div class = "col-md-4">
-      <div class = "well">
-        <layout-mainside
-          [sideWidth] = "mainsideSideWidth"
-          [showSide] = "mainsideShowSide">
-
-          <div maincontent>
-            MAINCONTENT <br >
-            <p>
-                Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
-            </p>
-          </div>
-          <div sidecontent>
-            SIDECONTENT
-          </div>
-        </layout-mainside>
-      </div>
-    </div>
-    <div class = "col-md-4">
-      
-    </div>
-  </div>
-</div>
\ No newline at end of file
diff --git a/src/layouts/mainside/mainside.animation.ts b/src/layouts/mainside/mainside.animation.ts
deleted file mode 100644
index cb19cf634e2707115bd7b108829885c646029619..0000000000000000000000000000000000000000
--- a/src/layouts/mainside/mainside.animation.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { animate, state, style, transition, trigger } from "@angular/animations";
-
-export const mainSideAnimation = trigger('collapseSide', [
-  state('collapsed',
-    style({
-      'flex-basis' : '0px',
-      'width' : '0px',
-    }),
-    { params : { sideWidth : 0, animationTiming: 180 } },
-  ),
-  state('visible',
-    style({
-      'flex-basis' : '{{ sideWidth }}px',
-      'width' : '{{ sideWidth }}px',
-    }),
-    { params : { sideWidth : 300, animationTiming: 180 } },
-  ),
-  transition('collapsed => visible', [
-    animate('{{ animationTiming }}ms ease-out'),
-  ]),
-  transition('visible => collapsed', [
-    animate('{{ animationTiming }}ms ease-in'),
-  ]),
-])
diff --git a/src/layouts/mainside/mainside.component.ts b/src/layouts/mainside/mainside.component.ts
deleted file mode 100644
index 624c75b69d2c1ceb1bdbc05ce52253b974ecde15..0000000000000000000000000000000000000000
--- a/src/layouts/mainside/mainside.component.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import { Component, EventEmitter, Input, Output } from "@angular/core";
-import { mainSideAnimation } from "./mainside.animation";
-
-@Component({
-  selector : 'layout-mainside',
-  templateUrl : './mainside.template.html',
-  styleUrls : [
-    './mainside.style.css',
-  ],
-  animations : [
-    mainSideAnimation,
-  ],
-})
-
-export class LayoutMainSide {
-  @Input() public showResizeSliver: boolean = true
-  @Input() public showSide: boolean = false
-  @Input() public sideWidth: number = 300
-  @Input() public animationFlag: boolean = false
-
-  @Output() public panelShowStateChanged: EventEmitter<boolean> = new EventEmitter()
-  @Output() public panelAnimationStart: EventEmitter<boolean> = new EventEmitter()
-  @Output() public panelAnimationEnd: EventEmitter<boolean> = new EventEmitter()
-
-  public togglePanelShow() {
-    this.showSide = !this.showSide
-    this.panelShowStateChanged.emit(this.showSide)
-  }
-
-  public animationStart() {
-    this.panelAnimationStart.emit(true)
-  }
-
-  public animationEnd() {
-    this.panelAnimationEnd.emit(true)
-  }
-}
diff --git a/src/layouts/mainside/mainside.style.css b/src/layouts/mainside/mainside.style.css
deleted file mode 100644
index da85c4123c2237cf74e7f25f062bdd15a57bba25..0000000000000000000000000000000000000000
--- a/src/layouts/mainside/mainside.style.css
+++ /dev/null
@@ -1,35 +0,0 @@
-:host
-{
-  display:block;
-  width:100%;
-  height:100%;
-  display:flex;
-  flex-direction: row;
-}
-
-div[maincontent]
-{
-  flex : 1 1 0px;
-  max-width:100%;
-  overflow-x:hidden;
-}
-
-div[resizeSliver]
-{
-  margin-top : 2em;
-  width:0em;
-}
-
-div[resizeSliver]
-{
-  z-index: 5;
-}
-
-div[sidecontent]
-{
-  flex : 0 0 0px;
-  box-shadow: 8px 0 8px 8px rgba(0,0,0,0.2);
-  z-index: 3;
-  overflow:hidden;
-}
-
diff --git a/src/layouts/mainside/mainside.template.html b/src/layouts/mainside/mainside.template.html
deleted file mode 100644
index ae93b37bc266219ef51992d08016ce2fc9ab9ea7..0000000000000000000000000000000000000000
--- a/src/layouts/mainside/mainside.template.html
+++ /dev/null
@@ -1,18 +0,0 @@
-<div maincontent>
-  <ng-content select="[maincontent]">
-  </ng-content>
-</div>
-
-<div resizeSliver>
-  <ng-content select = "[resizeSliver]">
-  </ng-content>
-</div>
-
-<div 
-  [@collapseSide] = "{ value : showSide ? 'visible' : 'collapsed' , params : { sideWidth : sideWidth, animationTiming : animationFlag ? 180 : 0 } }"
-  (@collapseSide.start) = "animationStart()"
-  (@collapseSide.done) = "animationEnd()"
-  sidecontent>
-  <ng-content select="[sidecontent]">
-  </ng-content>
-</div>
\ No newline at end of file
diff --git a/src/main-common.ts b/src/main-common.ts
index 6bab08d71f1a06e189d01db82b306d9fe098d49a..aebfaffc5d3208e82c686c67bacf27d9b332cef4 100644
--- a/src/main-common.ts
+++ b/src/main-common.ts
@@ -18,6 +18,11 @@ import '!!file-loader?context=src/res&name=icons/iav-icons.ttf!src/res/icons/iav
 import '!!file-loader?context=src/res&name=icons/iav-icons.woff!src/res/icons/iav-icons.woff'
 import '!!file-loader?context=src/res&name=icons/iav-icons.svg!src/res/icons/iav-icons.svg'
 
+/**
+ * favicons
+ */
+import '!!file-loader?context=src/res/favicons&name=favicon-128-light.png!src/res/favicons/favicon-128-light.png'
+
 import 'zone.js'
 import { enableProdMode } from '@angular/core';
 
diff --git a/src/main.module.ts b/src/main.module.ts
index a87d7e6d8fa662263b04876717cbcd98a0c480b0..cc25b011578ad18e3a9fb1e0d6f8fd21adc94742 100644
--- a/src/main.module.ts
+++ b/src/main.module.ts
@@ -1,6 +1,6 @@
 import { DragDropModule } from '@angular/cdk/drag-drop'
 import { CommonModule } from "@angular/common";
-import { CUSTOM_ELEMENTS_SCHEMA, InjectionToken, NgModule } from "@angular/core";
+import { NgModule } from "@angular/core";
 import { FormsModule } from "@angular/forms";
 import { StoreModule, ActionReducer } from "@ngrx/store";
 import { AngularMaterialModule } from 'src/ui/sharedModules/angularMaterial.module'
@@ -14,10 +14,10 @@ import { GetNamesPipe } from "./util/pipes/getNames.pipe";
 
 import { HttpClientModule } from "@angular/common/http";
 import { EffectsModule } from "@ngrx/effects";
-import { AtlasViewerAPIServices, CANCELLABLE_DIALOG, GET_TOAST_HANDLER_TOKEN, API_SERVICE_SET_VIEWER_HANDLE_TOKEN, setViewerHandleFactory, LOAD_MESH_TOKEN, ILoadMesh } from "./atlasViewer/atlasViewer.apiService.service";
+import { AtlasViewerAPIServices, CANCELLABLE_DIALOG, API_SERVICE_SET_VIEWER_HANDLE_TOKEN, setViewerHandleFactory } from "./atlasViewer/atlasViewer.apiService.service";
 import { AtlasWorkerService } from "./atlasViewer/atlasViewer.workerService.service";
-import { ModalUnit } from "./atlasViewer/modalUnit/modalUnit.component";
-import { TransformOnhoverSegmentPipe } from "./atlasViewer/onhoverSegment.pipe";
+import { LOAD_MESH_TOKEN, ILoadMesh, WINDOW_MESSAGING_HANDLER_TOKEN } from 'src/messaging/types'
+
 import { ConfirmDialogComponent } from "./components/confirmDialog/confirmDialog.component";
 import { DialogComponent } from "./components/dialog/dialog.component";
 import { DialogService } from "./services/dialogService.service";
@@ -26,28 +26,24 @@ import { LocalFileService } from "./services/localFile.service";
 import { NgViewerUseEffect } from "./services/state/ngViewerState.store";
 import { ViewerStateUseEffect } from "./services/state/viewerState.store";
 import { UIService } from "./services/uiService.service";
-import { DatabrowserModule, OVERRIDE_IAV_DATASET_PREVIEW_DATASET_FN, DataBrowserFeatureStore, GET_KGDS_PREVIEW_INFO_FROM_ID_FILENAME, DatabrowserService } from "src/ui/databrowserModule";
-import { ViewerStateControllerUseEffect } from "./ui/viewerStateController/viewerState.useEffect";
+import { DatabrowserModule, OVERRIDE_IAV_DATASET_PREVIEW_DATASET_FN, DataBrowserFeatureStore, GET_KGDS_PREVIEW_INFO_FROM_ID_FILENAME, DatabrowserService } from "src/atlasComponents/databrowserModule";
+import { ViewerStateControllerUseEffect } from "src/state";
 import { DockedContainerDirective } from "./util/directives/dockedContainer.directive";
-import { DragDropDirective } from "./util/directives/dragDrop.directive";
 import { FloatingContainerDirective } from "./util/directives/floatingContainer.directive";
 import { FloatingMouseContextualContainerDirective } from "./util/directives/floatingMouseContextualContainer.directive";
 import { NewViewerDisctinctViewToLayer } from "./util/pipes/newViewerDistinctViewToLayer.pipe";
-import { ClickInterceptor, CLICK_INTERCEPTOR_INJECTOR, UtilModule } from "src/util";
+import { ClickInterceptor, CLICK_INTERCEPTOR_INJECTOR, PureContantService, UtilModule } from "src/util";
 import { SpotLightModule } from 'src/spotlight/spot-light.module'
 import { TryMeComponent } from "./ui/tryme/tryme.component";
-import { MouseHoverDirective, MouseOverIconPipe, MouseOverTextPipe } from "./atlasViewer/mouseOver.directive";
 import { UiStateUseEffect } from "src/services/state/uiState.store";
-import { AtlasViewerHistoryUseEffect } from "./atlasViewer/atlasViewer.history.service";
 import { PluginServiceUseEffect } from './services/effect/pluginUseEffect';
 import { TemplateCoordinatesTransformation } from "src/services/templateCoordinatesTransformation.service";
 import { NewTemplateUseEffect } from './services/effect/newTemplate.effect';
 import { WidgetModule } from 'src/widget';
-import { PluginModule } from './atlasViewer/pluginUnit/plugin.module';
+import { PluginModule } from './plugin/plugin.module';
 import { LoggingModule } from './logging/logging.module';
-import { ShareModule } from './share';
 import { AuthService } from './auth'
-import { IAV_DATASET_PREVIEW_ACTIVE } from 'src/ui/databrowserModule'
+import { IAV_DATASET_PREVIEW_ACTIVE } from 'src/atlasComponents/databrowserModule'
 
 import 'hammerjs'
 import 'src/res/css/extra_styles.css'
@@ -55,9 +51,18 @@ import 'src/res/css/version.css'
 import 'src/theme.scss'
 import { DatasetPreviewGlue, datasetPreviewMetaReducer, IDatasetPreviewGlue, GlueEffects, ClickInterceptorService } from './glue';
 import { viewerStateHelperReducer, viewerStateMetaReducers, ViewerStateHelperEffect } from './services/state/viewerState.store.helper';
-import { TOS_OBS_INJECTION_TOKEN } from './ui/kgtos/kgtos.component';
+import { TOS_OBS_INJECTION_TOKEN } from './ui/kgtos';
 import { UiEffects } from './services/state/uiState/ui.effects';
 import { MesssagingModule } from './messaging/module';
+import { ParcellationRegionModule } from './atlasComponents/parcellationRegion';
+import { ViewerModule, VIEWERMODULE_DARKTHEME } from './viewerModule';
+import { CookieModule } from './ui/cookieAgreement/module';
+import { KgTosModule } from './ui/kgtos/module';
+import { MouseoverModule } from './mouseoverModule/mouseover.module';
+import { AtlasViewerRouterModule } from './routerModule';
+import { MessagingGlue } from './messagingGlue';
+import { BS_ENDPOINT } from './util/constants';
+import { QuickTourModule } from './ui/quickTour';
 
 export function debug(reducer: ActionReducer<any>): ActionReducer<any> {
   return function(state, action) {
@@ -83,10 +88,15 @@ export function debug(reducer: ActionReducer<any>): ActionReducer<any> {
     WidgetModule,
     PluginModule,
     LoggingModule,
-    ShareModule,
     MesssagingModule,
-
+    ViewerModule,
     SpotLightModule,
+    ParcellationRegionModule,
+    CookieModule,
+    KgTosModule,
+    MouseoverModule,
+    AtlasViewerRouterModule,
+    QuickTourModule,
     
     EffectsModule.forRoot([
       UseEffects,
@@ -95,7 +105,6 @@ export function debug(reducer: ActionReducer<any>): ActionReducer<any> {
       ViewerStateUseEffect,
       NgViewerUseEffect,
       PluginServiceUseEffect,
-      AtlasViewerHistoryUseEffect,
       UiStateUseEffect,
       NewTemplateUseEffect,
       ViewerStateHelperEffect,
@@ -121,26 +130,19 @@ export function debug(reducer: ActionReducer<any>): ActionReducer<any> {
   ],
   declarations : [
     AtlasViewer,
-    ModalUnit,
     TryMeComponent,
 
     /* directives */
     DockedContainerDirective,
     FloatingContainerDirective,
     FloatingMouseContextualContainerDirective,
-    DragDropDirective,
-    MouseHoverDirective, 
 
     /* pipes */
     GetNamesPipe,
     GetNamePipe,
-    TransformOnhoverSegmentPipe,
     NewViewerDisctinctViewToLayer,
-    MouseOverTextPipe,
-    MouseOverIconPipe,
   ],
   entryComponents : [
-    ModalUnit,
     DialogComponent,
     ConfirmDialogComponent,
   ],
@@ -153,13 +155,6 @@ export function debug(reducer: ActionReducer<any>): ActionReducer<any> {
     UIService,
     TemplateCoordinatesTransformation,
     ClickInterceptorService,
-    {
-      provide: GET_TOAST_HANDLER_TOKEN,
-      useFactory: (uiService: UIService) => {
-        return () => uiService.getToastHandler()
-      },
-      deps: [ UIService ]
-    },
     {
       provide: OVERRIDE_IAV_DATASET_PREVIEW_DATASET_FN,
       useFactory: (glue: IDatasetPreviewGlue) => glue.displayDatasetPreview.bind(glue),
@@ -235,8 +230,16 @@ export function debug(reducer: ActionReducer<any>): ActionReducer<any> {
       provide: CLICK_INTERCEPTOR_INJECTOR,
       useFactory: (clickIntService: ClickInterceptorService) => {
         return {
-          deregister: clickIntService.removeInterceptor.bind(clickIntService),
-          register: clickIntService.addInterceptor.bind(clickIntService)
+          deregister: clickIntService.deregister.bind(clickIntService),
+          register: (fn: (arg: any) => boolean, config?) => {
+            if (config?.last) {
+              clickIntService.register(fn)
+            } else {
+              clickIntService.register(fn, {
+                first: true
+              })
+            }
+          }
         } as ClickInterceptor
       },
       deps: [
@@ -251,14 +254,24 @@ export function debug(reducer: ActionReducer<any>): ActionReducer<any> {
       deps: [
         AtlasViewerAPIServices
       ]
-    }
+    },
+    {
+      provide: VIEWERMODULE_DARKTHEME,
+      useFactory: (pureConstantService: PureContantService) => pureConstantService.darktheme$,
+      deps: [ PureContantService ]
+    },
+    {
+      provide: WINDOW_MESSAGING_HANDLER_TOKEN,
+      useClass: MessagingGlue
+    },
+    {
+      provide: BS_ENDPOINT,
+      useValue: (BS_REST_URL || `https://siibra-api-latest.apps-dev.hbp.eu/v1_0`).replace(/\/$/, '')
+    },
   ],
   bootstrap : [
     AtlasViewer,
   ],
-  schemas: [
-    CUSTOM_ELEMENTS_SCHEMA,
-  ],
 })
 
 export class MainModule {
diff --git a/src/messaging/module.spec.ts b/src/messaging/module.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0718eb33d55cec48f03ce267fcd76be3e214373f
--- /dev/null
+++ b/src/messaging/module.spec.ts
@@ -0,0 +1,54 @@
+import { CommonModule } from "@angular/common"
+import { Component } from "@angular/core"
+import { TestBed } from "@angular/core/testing"
+import { provideMockStore } from "@ngrx/store/testing"
+import { MesssagingModule } from "./module"
+import { IAV_POSTMESSAGE_NAMESPACE } from './service'
+
+@Component({
+  template: ''
+})
+class DummyCmp{}
+
+describe('> module.ts', () => {
+  describe('> MesssagingModule', () => {
+    beforeEach(() => {
+      TestBed.configureTestingModule({
+        imports: [
+          CommonModule,
+          MesssagingModule
+        ],
+        declarations: [
+          DummyCmp
+        ],
+        providers: [
+          provideMockStore()
+        ]
+      }).compileComponents()
+    })
+
+    describe('> service is init', () => {
+      let spy: jasmine.Spy
+      beforeEach(() => {
+      })
+
+      // TODO need to test that module result in service instantiation
+      it('> pong is heard', () => {
+        // const fixture = TestBed.createComponent(DummyCmp)
+
+        // spy = jasmine.createSpy()
+        // window.addEventListener('message', ev => {
+        //   console.log('message', ev.data)
+        //   if (ev.data.result === 'pong') {
+        //     spy()
+        //   }
+        // })
+        // window.postMessage({
+        //   method: `${IAV_POSTMESSAGE_NAMESPACE}ping`,
+        //   id: '123'
+        // }, '*' )
+        // expect(spy).toHaveBeenCalled()
+      })
+    })
+  })
+})
diff --git a/src/messaging/module.ts b/src/messaging/module.ts
index 9e5152f3d6c62c9aa8442aab8362ab1d964f4bb6..bb0f9aac2a7f53d9f8cd258b687a49b3bb709783 100644
--- a/src/messaging/module.ts
+++ b/src/messaging/module.ts
@@ -1,150 +1,23 @@
-import { Inject, NgModule, Optional } from "@angular/core";
-import { MatDialog } from "@angular/material/dialog";
-import { MatSnackBar } from "@angular/material/snack-bar";
-import { AtlasViewerAPIServices } from "src/atlasViewer/atlasViewer.apiService.service";
-import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service";
-import { LOAD_MESH_TOKEN, ILoadMesh } from "src/atlasViewer/atlasViewer.apiService.service";
+import { NgModule } from "@angular/core";
 import { ComponentsModule } from "src/components";
-import { ConfirmDialogComponent } from "src/components/confirmDialog/confirmDialog.component";
+
 import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module";
+import { MessagingService } from "./service";
 
-const IAV_POSTMESSAGE_NAMESPACE = `ebrains:iav:`
 
 @NgModule({
   imports: [
     AngularMaterialModule,
     ComponentsModule,
+  ],
+  providers: [
+    MessagingService,
   ]
 })
 
 export class MesssagingModule{
-
-  private whiteListedOrigins = new Set()
-  private pendingRequests: Map<string, Promise<boolean>> = new Map()
-
-  constructor(
-    private dialog: MatDialog,
-    private snackbar: MatSnackBar,
-    private worker: AtlasWorkerService,
-    @Optional() private apiService: AtlasViewerAPIServices,
-    @Optional() @Inject(LOAD_MESH_TOKEN) private loadMesh: (loadMeshParam: ILoadMesh) => void
-  ){
-
-    window.addEventListener('message', async ({ data, origin, source }) => {
-      const { method, id, param } = data
-      const src = source as Window
-      if (!method) return
-      if (method.indexOf(IAV_POSTMESSAGE_NAMESPACE) !== 0) return
-      const strippedMethod = method.replace(IAV_POSTMESSAGE_NAMESPACE, '')
-
-      /**
-       * if ping method, respond pong method
-       */
-      if (strippedMethod === 'ping') {
-        window.opener.postMessage({
-          id,
-          result: 'pong',
-          jsonrpc: '2.0'
-        }, origin)
-        return
-      }
-
-      /**
-       * otherwise, check permission
-       */
-
-      try {
-        const allow = await this.checkOrigin({ origin })
-        if (!allow) {
-          src.postMessage({
-            jsonrpc: '2.0',
-            id,
-            error: {
-              code: 403,
-              message: 'User declined'
-            }
-          }, origin)
-          return
-        }
-        const result = await this.processMessage({ method: strippedMethod, param })
-
-        src.postMessage({
-          jsonrpc: '2.0',
-          id,
-          result
-        }, origin)
-
-      } catch (e) {
-
-        src.postMessage({
-          jsonrpc: '2.0',
-          id,
-          error: e.code
-            ? e
-            : { code: 500, message: e.toString() }
-        }, origin)
-      }
-
-    })
-  }
-
-  async processMessage({ method, param }){
-
-    if (method === 'dummyMethod') {
-      return 'OK'
-    }
-
-    if (method === 'viewerHandle:add3DLandmarks') {
-      this.apiService.interactiveViewer.viewerHandle.add3DLandmarks(param)
-      return 'OK'
-    }
-
-    if (method === 'viewerHandle:remove3DLandmarks') {
-      this.apiService.interactiveViewer.viewerHandle.remove3DLandmarks(param)
-      return 'OK'
-    }
-
-    if (method === '_tmp:plotly') {
-      const isLoadingSnack = this.snackbar.open(`Loading plotly mesh ...`)
-      const resp = await this.worker.sendMessage({
-        method: `PROCESS_PLOTLY`,
-        param
-      })
-      isLoadingSnack?.dismiss()
-      const meshId = 'bobby'
-      if (this.loadMesh) {
-        const { objectUrl } = resp.result || {}
-        this.loadMesh({
-          type: 'VTK',
-          id: meshId,
-          url: objectUrl
-        })
-      } else {
-        this.snackbar.open(`Error: loadMesh method not injected.`)
-      }
-      return 'OK'
-    }
-
-    throw ({ code: 404, message: 'Method not found' })
-  }
-
-  async checkOrigin({ origin }){
-    if (this.whiteListedOrigins.has(origin)) return true
-    if (this.pendingRequests.has(origin)) return this.pendingRequests.get(origin)
-    const responsePromise = this.dialog.open(
-      ConfirmDialogComponent,
-      {
-        data: {
-          title: `Cross tab messaging`,
-          message: `${origin} would like to send data to interactive atlas viewer`,
-          okBtnText: `Allow`
-        }
-      }
-    ).afterClosed().toPromise()
-    this.pendingRequests.set(origin, responsePromise)
-    const response = await responsePromise
-    this.pendingRequests.delete(origin)
-    if (response) this.whiteListedOrigins.add(origin)
-    return response
+  // need to inject service
+  // eslint-disable-next-line @typescript-eslint/no-empty-function
+  constructor(_mS: MessagingService){
   }
-}
\ No newline at end of file
+}
diff --git a/src/messaging/native/index.ts b/src/messaging/native/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..394f5418658eb6ac1ed44621ccf1d9c024f24d33
--- /dev/null
+++ b/src/messaging/native/index.ts
@@ -0,0 +1,27 @@
+import { Observable, Subject } from "rxjs"
+import { IMessagingActions, IMessagingActionTmpl } from "../types"
+
+export const TYPE = 'iav.unload'
+
+type TUnload = {
+  ['@id']: string
+}
+
+export const processJsonLd = (json: TUnload): Observable<IMessagingActions<keyof IMessagingActionTmpl>> => {
+  const sub = new Subject<IMessagingActions<keyof IMessagingActionTmpl>>()
+  const _main = (() => {
+    if (!json['@id']) {
+      return sub.error(`@id must be defined to `)
+    }
+    sub.next({
+      type: 'unloadResource',
+      payload: {
+        "@id": json['@id']
+      }
+    })
+    sub.complete()
+
+  })
+  setTimeout(_main);
+  return sub
+}
diff --git a/src/messaging/nmvSwc/index.spec.ts b/src/messaging/nmvSwc/index.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..86862ed2a2a3eda0898f813ec8f083c94025421b
--- /dev/null
+++ b/src/messaging/nmvSwc/index.spec.ts
@@ -0,0 +1,66 @@
+import { IAV_IDS, IAV_VOXEL_SIZES_NM } from './index'
+
+const waxholmTemplates = require('!json-loader!src/res/ext/waxholmRatV2_0.json')
+const allenTemplates = require('!json-loader!src/res/ext/allenMouse.json')
+const colinTemplates = require('!json-loader!src/res/ext/colin.json')
+const mniTemplates = require('!json-loader!src/res/ext/MNI152.json')
+const bbTemplates = require('!json-loader!src/res/ext/bigbrain.json')
+
+
+const ratNehubaConfig = require('!json-loader!src/res/ext/waxholmRatV2_0NehubaConfig.json')
+const mouseNehubaConfig = require('!json-loader!src/res/ext/allenMouseNehubaConfig.json')
+const colinNehubaConfig = require('!json-loader!src/res/ext/colinNehubaConfig.json')
+const icbmNehubaConfig = require('!json-loader!src/res/ext/MNI152NehubaConfig.json')
+const bbNehubaConfig = require('!json-loader!src/res/ext/bigbrainNehubaConfig.json')
+
+const tmplArr = [
+  waxholmTemplates,
+  allenTemplates,
+  colinTemplates,
+  mniTemplates,
+  bbTemplates,
+]
+
+const configArr = [
+  ratNehubaConfig,
+  mouseNehubaConfig,
+  colinNehubaConfig,
+  icbmNehubaConfig,
+  bbNehubaConfig,
+].map(cfg => {
+
+  return {
+    layerNames: Object.keys(
+      cfg['dataset']['initialNgState']['layers']
+    ),
+    voxelSize: cfg['dataset']['initialNgState']['navigation']['pose']['position']['voxelSize']
+  }
+})
+
+describe('> messaging/nmvSwc', () => {
+  for (const tmplKey in IAV_IDS) {
+    describe(`> ${tmplKey}`, () => {
+      let tmpl, config
+      beforeAll(() => {
+        tmpl = tmplArr.find(t => t['@id'] === IAV_IDS[tmplKey])
+        config = tmpl && configArr.find(cfg => 
+          cfg.layerNames.includes(tmpl['ngId'])
+        )
+      })
+
+      it('> should be able to find tmpl by id', () => {
+        expect(tmpl).toBeTruthy()
+      })
+
+      it('> should be able to find the config', () => {
+        expect(config).toBeTruthy()
+      })
+
+      it('> voxelSize should match the hardcoded value', () => {
+        expect(
+          config.voxelSize
+        ).toEqual(IAV_VOXEL_SIZES_NM[tmplKey])
+      })
+    })
+  }
+})
diff --git a/src/messaging/nmvSwc/index.ts b/src/messaging/nmvSwc/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..08b0ce6994d4ad595d3aa1689e872e8d45a702b8
--- /dev/null
+++ b/src/messaging/nmvSwc/index.ts
@@ -0,0 +1,183 @@
+import { Observable, Subject } from "rxjs"
+import { getUuid } from "src/util/fn"
+import { IMessagingActions, IMessagingActionTmpl, TVec4, TMat4 } from "../types"
+import { INmvTransform } from "./type"
+
+export const TYPE = 'bas:datasource'
+
+const waitFor = (condition: (...arg: any[]) => boolean) => new Promise<void>((rs, rj) => {
+  const intervalRef = setInterval(() => {
+    if (condition()) {
+      clearInterval(intervalRef)
+      rs()
+    }
+  }, 1000)
+})
+
+const NM_IDS = {
+  AMBA_V3: 'hbp:Allen_Mouse_CCF_v3(um)',
+  WAXHOLM_V1_01: 'hbp:WHS_SD_Rat_v1.01(um)',
+  BIG_BRAIN: 'hbp:BigBrain_r2015(um)',
+  COLIN: 'hbp:Colin27_r2008(um)',
+  MNI152_2009C_ASYM: 'hbp:ICBM_Asym_r2009c(um)',
+}
+
+export const IAV_IDS = {
+  AMBA_V3: 'minds/core/referencespace/v1.0.0/265d32a0-3d84-40a5-926f-bf89f68212b9',
+  WAXHOLM_V1_01: 'minds/core/referencespace/v1.0.0/d5717c4a-0fa1-46e6-918c-b8003069ade8',
+  BIG_BRAIN: 'minds/core/referencespace/v1.0.0/a1655b99-82f1-420f-a3c2-fe80fd4c8588',
+  COLIN: 'minds/core/referencespace/v1.0.0/7f39f7be-445b-47c0-9791-e971c0b6d992',
+  MNI152_2009C_ASYM: 'minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2',
+}
+
+/**
+ * TODO, should unify navigation voxelSize
+ * the emitted voxelCoord should be calculated on the fly
+ */
+export const IAV_VOXEL_SIZES_NM = {
+  AMBA_V3: [
+    25000,
+    25000,
+    25000
+  ],
+  WAXHOLM_V1_01: [
+    39062.5,
+    39062.5,
+    39062.5
+  ],
+  BIG_BRAIN: [
+    21166.666015625,
+    20000,
+    21166.666015625
+  ],
+  COLIN: [
+    1000000,
+    1000000,
+    1000000,
+  ],
+  MNI152_2009C_ASYM: [
+    1000000,
+    1000000,
+    1000000
+  ]
+}
+
+const translateSpace = (spaceId: string) => {
+  for (const key in NM_IDS){
+    if (NM_IDS[key] === spaceId) return IAV_IDS[key]
+  }
+  return null
+}
+
+const getVoxelFromSpace = (spaceId: string) => {
+  for (const key in NM_IDS){
+    if (NM_IDS[key] === spaceId) return IAV_VOXEL_SIZES_NM[key]
+  }
+  return null
+}
+
+export const processJsonLd = (json: { [key: string]: any }): Observable<IMessagingActions<keyof IMessagingActionTmpl>> => {
+  const subject = new Subject<IMessagingActions<keyof IMessagingActionTmpl>>()
+  const main = (async () => {
+
+    const {
+      encoding,
+      mediaType,
+      data: rawData,
+      transformations
+    } = json
+
+    if (mediaType.indexOf('model/swc') < 0) return subject.error(`mediaType of ${mediaType} cannot be parsed. Not 'model/swc'`)
+
+    if (!Array.isArray(transformations)) {
+      return subject.error(`transformations must be an array!`)
+    }
+
+    if (!transformations[0]) {
+      return subject.error(`transformations[0] must be defined`)
+    }
+    const { toSpace, params } = transformations[0] as INmvTransform
+    const { A, b } = params
+
+    const iavSpace = translateSpace(toSpace)
+    if (!iavSpace) {
+      return subject.error(`toSpace with id ${toSpace} cannot be found.`)
+    }
+    subject.next({
+      type: 'loadTemplate',
+      payload: {
+        ['@id']: iavSpace
+      }
+    })
+
+    await waitFor(() => !!(window as any).export_nehuba)
+
+    const b64Encoded = encoding.indexOf('base64') >= 0
+    const isGzipped = encoding.indexOf('gzip') >= 0
+    let data = rawData
+    if (b64Encoded) {
+      data = atob(data)
+    }
+    if (isGzipped) {
+      data = (window as any).export_nehuba.pako.inflate(data)
+    }
+    let output = ``
+    for (let i = 0; i < data.length; i++) {
+      output += String.fromCharCode(data[i])
+    }
+
+    const encoder = new TextEncoder()
+    const tmpUrl = URL.createObjectURL(
+      new Blob([ encoder.encode(output) ], { type: 'application/octet-stream' })
+    )
+    const uuid = getUuid()
+
+    // NG internal treats skeleton as mm
+    const voxelSize = getVoxelFromSpace(toSpace)
+    /**
+     * swc seem to scale with voxelSize... strangely enough
+     * voxelSize nm / voxel -> goal is 1 voxel/um
+     * 1e3 / voxelSize
+     */
+    const scaleUmToVoxelFixed = [
+      1e3 / voxelSize[0],
+      1e3 / voxelSize[1],
+      1e3 / voxelSize[2],
+    ]
+    // NG translation works on nm scale
+    const scaleUmToNm = 1e3
+    const { mat3, vec3 } = (window as any).export_nehuba
+    const modA = mat3.fromValues(
+      scaleUmToVoxelFixed[0], 0, 0,
+      0, scaleUmToVoxelFixed[1], 0,
+      0, 0, scaleUmToVoxelFixed[2]
+    )
+    mat3.mul(modA, modA, [...A[0], ...A[1], ...A[2]])
+    const modb = vec3.scale(vec3.create(), b, scaleUmToNm)
+    const transform = [
+      [...modA.slice(0, 3), modb[0]] as TVec4,
+      [...modA.slice(3, 6), modb[1]] as TVec4,
+      [...modA.slice(6), modb[2]] as TVec4,
+      [0, 0, 0, 1],
+    ] as TMat4
+    const payload: IMessagingActionTmpl['loadResource'] = {
+      '@id': uuid,
+      "@type" : 'swc',
+      unload: () => {
+        URL.revokeObjectURL(tmpUrl)
+      },
+      url: tmpUrl,
+      resourceParam: {
+        transform
+      }
+    }
+    subject.next({
+      type: 'loadResource',
+      payload
+    })
+    
+    subject.complete()
+  })
+  setTimeout(main);
+  return subject
+}
diff --git a/src/messaging/nmvSwc/type.ts b/src/messaging/nmvSwc/type.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9254e5264d0477bd134658e4ed6cc9dc05515c20
--- /dev/null
+++ b/src/messaging/nmvSwc/type.ts
@@ -0,0 +1,11 @@
+import { TMat3, TVec3 } from "../types";
+
+export interface INmvTransform {
+  ['@type']: 'bas:AffineTransformation'
+  fromSpace: string
+  toSpace: string
+  params: {
+    A: TMat3
+    b: TVec3
+  }
+}
\ No newline at end of file
diff --git a/src/messaging/service.spec.ts b/src/messaging/service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..46832b1e370c7ed4fa90846de4dc9eb6d4b93fd6
--- /dev/null
+++ b/src/messaging/service.spec.ts
@@ -0,0 +1,357 @@
+import { TestBed } from "@angular/core/testing"
+import { MockStore, provideMockStore } from "@ngrx/store/testing"
+import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service"
+import { viewerStateFetchedAtlasesSelector } from "src/services/state/viewerState/selectors"
+import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module"
+import { getUuid } from "src/util/fn"
+import { IAV_POSTMESSAGE_NAMESPACE, MessagingService } from "./service"
+import { IMessagingActions, IMessagingActionTmpl, WINDOW_MESSAGING_HANDLER_TOKEN } from "./types"
+import { MANAGED_METHODS } from './service'
+import { of, Subject } from "rxjs"
+import { ConfirmDialogComponent } from "src/components/confirmDialog/confirmDialog.component"
+import { TYPE as NATIVE_TYPE } from './native'
+
+describe('> service.ts', () => {
+  describe('> MessagingService', () => {
+    const windowMessagehandler = {
+      loadResource: jasmine.createSpy(),
+      loadTempladById: jasmine.createSpy(),
+      unloadResource: jasmine.createSpy()
+    }
+    afterEach(() => {
+      windowMessagehandler.loadResource.calls.reset()
+      windowMessagehandler.unloadResource.calls.reset()
+      windowMessagehandler.loadTempladById.calls.reset()
+    })
+    beforeEach(() => {
+
+      TestBed.configureTestingModule({
+        imports: [
+          AngularMaterialModule,
+        ],
+        providers: [
+          provideMockStore(),
+          AtlasWorkerService,
+          {
+            provide: WINDOW_MESSAGING_HANDLER_TOKEN,
+            useValue: windowMessagehandler
+          }
+        ]
+      })
+
+      const mockStore = TestBed.inject(MockStore)
+      mockStore.overrideSelector(viewerStateFetchedAtlasesSelector, [])
+    })
+
+    it('> can be inst', () => {
+      const s = TestBed.inject(MessagingService)
+      expect(s).toBeTruthy()
+    })
+
+    describe('> on construct', () => {
+      describe('> if window.opener', () => {
+        describe('> is defined', () => {
+          let openerProxy = {
+            postMessage: jasmine.createSpy()
+          }
+          const randomWindowName = getUuid()
+          beforeEach(() => {
+            spyOnProperty(window, 'opener').and.returnValue(openerProxy)
+            spyOnProperty(window, 'name').and.returnValue(randomWindowName)
+            TestBed.inject(MessagingService)
+          })
+          afterEach(() => {
+            openerProxy.postMessage.calls.reset()
+          })
+          it('> postMessage is called on window.opener', () => {
+            expect(openerProxy.postMessage).toHaveBeenCalledTimes(1)
+          })
+          describe('> args are as expected', () => {
+            let args: any[]
+            beforeEach(() => {
+              args = openerProxy.postMessage.calls.allArgs()[0]
+            })
+            it('> method === {namespace}onload', () => {
+              expect(args[0]['method']).toEqual(`${IAV_POSTMESSAGE_NAMESPACE}onload`)
+            })
+            it('> param[window.name] is windowname', () => {
+              expect(args[0]['param']['window.name']).toEqual(randomWindowName)
+            })
+          })
+
+          describe('> beforeunload', () => {
+            beforeEach(() => {
+              // onload messages are called before unload
+              openerProxy.postMessage.calls.reset()
+
+              // https://github.com/karma-runner/karma/issues/1062#issuecomment-42421624
+              window.onbeforeunload = null
+              window.dispatchEvent(new Event('beforeunload'))
+            })
+            it('> sends beforeunload event', () => {
+              expect(openerProxy.postMessage).toHaveBeenCalled()
+            })
+            it('> method is {namespace}beforeunload', () => {
+              const args = openerProxy.postMessage.calls.allArgs()[0]
+              expect(args[0]['method']).toEqual(`${IAV_POSTMESSAGE_NAMESPACE}beforeunload`)
+            })
+          })
+        })
+      })
+    
+    })
+
+    describe('> #handleMessage', () => {
+      let mService: MessagingService
+      let checkOriginSpy: jasmine.Spy
+      let processMessageSpy: jasmine.Spy
+
+      beforeEach(() => {
+        mService = TestBed.inject(MessagingService)
+      })
+
+      afterEach(() => {
+        checkOriginSpy && checkOriginSpy.calls.reset()
+        processMessageSpy && processMessageSpy.calls.reset()
+      })
+      describe('> ping', () => {
+        beforeEach(() => {
+          checkOriginSpy = spyOn(mService, 'checkOrigin').and.callFake(() => Promise.reject('baz'))
+        })
+        it('> pong', async () => {
+          const result = await mService.handleMessage({
+            data: {
+              method: `${IAV_POSTMESSAGE_NAMESPACE}ping`
+            },
+            origin: 'foobar'
+          })
+          expect(result).toEqual('pong')
+          expect(checkOriginSpy).not.toHaveBeenCalled()
+        })
+      })
+
+      describe('> misc method', () => {
+        let result: any
+        const expectedResult = 'helloworld'
+        const origin = 'foobar'
+        const method = `${IAV_POSTMESSAGE_NAMESPACE}${MANAGED_METHODS[0]}`
+        const param = {
+          'foo': 'bar'
+        }
+        describe('> if checkOrigin succeeds', () => {
+          beforeEach(async () => {
+            checkOriginSpy = spyOn(mService, 'checkOrigin').and.callFake(() => Promise.resolve(true))
+            processMessageSpy = spyOn(mService, 'processMessage').and.callFake(() => Promise.resolve(expectedResult))
+
+            result = await mService.handleMessage({
+              data: { method, param },
+              origin
+            })
+          })
+          it('> should call checkOrigin', () => {
+            expect(checkOriginSpy).toHaveBeenCalledWith({
+              origin
+            })
+          })
+
+          it('> should call processMessageSpy', () => {
+            expect(processMessageSpy).toHaveBeenCalledWith({
+              method: MANAGED_METHODS[0],
+              param
+            })
+          })
+
+          it('> should return result', () => {
+            expect(result).toEqual(expectedResult)
+          })
+        })
+
+        describe('> if checkOrigin fails', () => {
+
+          beforeEach(() => {
+            checkOriginSpy = spyOn(mService, 'checkOrigin').and.callFake(() => Promise.resolve(false))
+            processMessageSpy = spyOn(mService, 'processMessage').and.callFake(() => Promise.resolve(expectedResult))
+
+          })
+
+          it('> function should throw', async () => {
+            try {
+              await mService.handleMessage({
+                data: { method, param },
+                origin
+              })
+              expect(true).toEqual(false)
+            } catch (e) {
+              expect(true).toEqual(true)
+            }
+          })
+
+          it('> processMessage should not be called', async () => {
+            try {
+              await mService.handleMessage({
+                data: { method, param },
+                origin
+              })
+            } catch (e) {
+
+            }
+            expect(processMessageSpy).not.toHaveBeenCalled()
+          })
+        })
+
+      })
+
+      it('> if throws')
+    })
+
+    describe('> #checkOrigin', () => {
+      let ms: MessagingService
+      let dialogOpenSpy: jasmine.Spy
+      let r1: any
+      const origin = 'foobar'
+      beforeEach(() => {
+        ms = TestBed.inject(MessagingService)
+      })
+      
+      describe('> if dialog returns true', () => {
+        beforeEach(async () => {
+          dialogOpenSpy = spyOn(ms['dialog'], 'open').and.returnValue({
+            afterClosed: () => of(true)
+          } as any)
+          r1 = await ms.checkOrigin({ origin })
+        })
+        afterEach(() => {
+          dialogOpenSpy.calls.reset()
+        })
+        it('> should return true', () => {
+          expect(r1).toEqual(true)
+        })
+        it('> should call dialogOpen', () => {
+          expect(dialogOpenSpy).toHaveBeenCalledWith(
+            ConfirmDialogComponent,
+            {
+              data: {
+                title: 'Cross tab messaging',
+                message: `${origin} would like to send data to interactive atlas viewer`,
+                okBtnText: `Allow`,
+              }
+            }
+          )
+        })
+        it('> should not call dialogOpen multiple times', async () => {
+          dialogOpenSpy.calls.reset()
+          const r2 = await ms.checkOrigin({ origin })
+          expect(r2).toEqual(true)
+          expect(dialogOpenSpy).not.toHaveBeenCalled()
+        })
+      })
+    })
+  
+    describe('> #processMessage', () => {
+      // to be deprecated
+    })
+
+    describe('> #processJsonld', () => {
+      let ms: MessagingService
+      let sub: Subject<IMessagingActions<keyof IMessagingActionTmpl>>
+      const jsonLd = { '@type': 'foobar' }
+      beforeEach(() => {
+        ms = TestBed.inject(MessagingService)
+        sub = new Subject()
+        spyOn(ms['typeRegister'], 'get').and.returnValue(() => sub)
+      })
+
+      it('> resolves if sub completes', done => {
+        ms.processJsonld(jsonLd)
+          .then(() => {
+            done()
+          })
+          .catch(() => {
+            expect(true).toEqual(false)
+            done()
+          })
+
+        sub.complete()
+      })
+    
+      it('> rejects if sub throws', done => {
+
+        ms.processJsonld(jsonLd)
+          .then(() => {
+            expect(true).toEqual(false)
+            done()
+          })
+          .catch(() => {
+            done()
+          })
+
+        sub.error(`foobazz`)
+      })
+    
+      describe('> functions', () => {
+        let pr: Promise<any>
+        beforeEach(() => {
+          pr = ms.processJsonld(jsonLd)
+        })
+
+        describe('> loadTemplate', () => {
+          beforeEach(() => {
+            sub.next({
+              type: "loadTemplate",
+              payload: {
+                ['@id']: 'foobar'
+              }
+            })
+          })
+          it('> calls windowsMessagehandler.loadTemplateById', () => {
+            expect(windowMessagehandler.loadTempladById).toHaveBeenCalledWith({
+              ['@id']: 'foobar'
+            })
+          })
+        })
+      
+        describe('> loadResources', () => {
+          const payload = {
+            ['@id']: 'rez',
+            ['@type']: 'hello world'
+          }
+          beforeEach(() => {
+            sub.next({
+              type: 'loadResource',
+              payload
+            })
+          })
+          it('> calls windowsMessagehandler.loadResource', () => {
+            expect(windowMessagehandler.loadResource).toHaveBeenCalledWith(payload)
+          })
+          it('> expects processJsonLd to resolve to id/type', done => {
+            pr.then(val => {
+              expect(val).toEqual({
+                ['@id']: payload['@id'],
+                ['@type']: NATIVE_TYPE
+              })
+              done()
+            })
+            sub.complete()
+          })
+        })
+
+        describe('> unloadResource', () => {
+          const payload = {
+            ['@id']: 'greenday'
+          }
+          beforeEach(() => {
+
+            sub.next({
+              type: 'unloadResource',
+              payload
+            })
+          })
+          it('> calls windowsMessagehandler.unloadResource', () => {
+            expect(windowMessagehandler.unloadResource).toHaveBeenCalledWith(payload)
+          })
+        })
+      })
+    })
+  })
+})
diff --git a/src/messaging/service.ts b/src/messaging/service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8e73122c8ce67553be6736b96c92d8912f1510a1
--- /dev/null
+++ b/src/messaging/service.ts
@@ -0,0 +1,231 @@
+import { Inject, Injectable, Optional } from "@angular/core";
+import { Observable } from "rxjs";
+import { MatDialog } from "@angular/material/dialog";
+import { MatSnackBar } from "@angular/material/snack-bar";
+
+import { getUuid } from "src/util/fn";
+import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service";
+import { ConfirmDialogComponent } from "src/components/confirmDialog/confirmDialog.component";
+
+import { IMessagingActions, IMessagingActionTmpl, ILoadMesh, LOAD_MESH_TOKEN, WINDOW_MESSAGING_HANDLER_TOKEN, IWindowMessaging } from './types'
+import { TYPE as NMV_TYPE, processJsonLd as nmvProcess } from './nmvSwc/index'
+import { TYPE as NATIVE_TYPE, processJsonLd as nativeProcess } from './native'
+
+export const IAV_POSTMESSAGE_NAMESPACE = `ebrains:iav:`
+
+export const MANAGED_METHODS = [
+  'openminds:nmv:loadSwc',
+  'openminds:nmv:unloadSwc'
+]
+
+@Injectable({
+  providedIn: 'root'
+})
+
+export class MessagingService {
+
+  private whiteListedOrigins = new Set()
+  private pendingRequests: Map<string, Promise<boolean>> = new Map()
+  private windowName: string
+
+  private typeRegister: Map<string, (arg: any) => Observable<IMessagingActions<keyof IMessagingActionTmpl>>> = new Map()
+  
+  constructor(
+    private dialog: MatDialog,
+    private snackbar: MatSnackBar,
+    private worker: AtlasWorkerService,
+    @Optional() @Inject(WINDOW_MESSAGING_HANDLER_TOKEN) private messagingHandler: IWindowMessaging,
+    @Optional() @Inject(LOAD_MESH_TOKEN) private loadMesh: (loadMeshParam: ILoadMesh) => void,
+  ){
+    
+    if (window.opener){
+      this.windowName = window.name
+      window.opener.postMessage({
+        id: getUuid(),
+        method: `${IAV_POSTMESSAGE_NAMESPACE}onload`,
+        param: {
+          'window.name': this.windowName
+        }
+      }, '*')
+
+      window.addEventListener('beforeunload', () => {
+        window.opener.postMessage({
+          id: getUuid(),
+          method: `${IAV_POSTMESSAGE_NAMESPACE}beforeunload`,
+          param: {
+            'window.name': this.windowName
+          }
+        }, '*')
+      })
+    }
+
+    window.addEventListener('message', async ({ data, origin, source }) => {
+      
+      const src = source as Window
+      const { id } = data
+      try {
+        const result = await this.handleMessage({ data, origin })
+        src.postMessage({
+          id,
+          jsonrpc: '2.0',
+          result
+        }, origin)
+      } catch (error) {
+        src.postMessage({
+          id,
+          jsonrpc: '2.0',
+          error
+        }, origin)
+      }
+    })
+
+    this.typeRegister.set(
+      NMV_TYPE,
+      nmvProcess
+    )
+    this.typeRegister.set(
+      NATIVE_TYPE,
+      nativeProcess
+    )
+
+  }
+
+  public async handleMessage({ data, origin }) {
+    const { method, param } = data
+    
+    if (!method) return
+    if (method.indexOf(IAV_POSTMESSAGE_NAMESPACE) !== 0) return
+    const strippedMethod = method.replace(IAV_POSTMESSAGE_NAMESPACE, '')
+
+    /**
+     * if ping method, respond pong method
+     */
+    if (strippedMethod === 'ping') {
+      return 'pong'
+    }
+
+    /**
+     * otherwise, check permission
+     */
+
+    const allow = await this.checkOrigin({ origin })
+    if (!allow) throw ({
+      code: 403,
+      message: 'User declined'
+    })
+
+    // TODO 
+    // in future, check if in managed_methods
+    // if yes, directly call processJsonld
+    // if not, directly throw 
+
+    return await this.processMessage({ method: strippedMethod, param })
+  }
+
+  processJsonld(jsonLd: any){
+    const { ['@type']: type } = jsonLd
+    const fn = this.typeRegister.get(type)
+    if (!fn) {
+      return Promise.reject(`${type} does not have a handler registered.`)
+    }
+    let returnValue: any = {}
+    return new Promise((rs, rj) => {
+
+      const sub = fn(jsonLd)
+      sub.subscribe(
+        ev => {
+          if (ev.type === 'loadTemplate') {
+            const payload = ev.payload as IMessagingActionTmpl['loadTemplate']
+            
+            this.messagingHandler.loadTempladById(payload)
+          }
+
+          if (ev.type === 'loadResource') {
+            const payload = ev.payload as IMessagingActionTmpl['loadResource']
+            returnValue = {
+              ['@id']: payload["@id"],
+              ['@type']: NATIVE_TYPE
+            }
+            this.messagingHandler.loadResource(payload)
+          }
+
+          if (ev.type === 'unloadResource') {
+            const payload = ev.payload as IMessagingActionTmpl['unloadResource']
+            this.messagingHandler.unloadResource(payload)
+          }
+        },
+        rj,
+        () => {
+          rs(returnValue || {})
+        }
+      )
+    })
+  }
+
+  async processMessage({ method, param }){
+
+    // TODO combine api service and messaging service into one
+    // and implement it properly
+
+    // if (method === 'viewerHandle:add3DLandmarks') {
+    //   this.apiService.interactiveViewer.viewerHandle.add3DLandmarks(param)
+    //   return 'OK'
+    // }
+
+    // if (method === 'viewerHandle:remove3DLandmarks') {
+    //   this.apiService.interactiveViewer.viewerHandle.remove3DLandmarks(param)
+    //   return 'OK'
+    // }
+
+    /**
+     * TODO use loadResource in the future
+     */
+    if (method === '_tmp:plotly') {
+      const isLoadingSnack = this.snackbar.open(`Loading plotly mesh ...`)
+      const resp = await this.worker.sendMessage({
+        method: `PROCESS_PLOTLY`,
+        param
+      })
+      isLoadingSnack?.dismiss()
+      const meshId = 'bobby'
+      if (this.loadMesh) {
+        const { objectUrl, customFragmentColor } = resp.result || {}
+        this.loadMesh({
+          type: 'VTK',
+          id: meshId,
+          url: objectUrl,
+          customFragmentColor
+        })
+      } else {
+        this.snackbar.open(`Error: loadMesh method not injected.`)
+      }
+      return 'OK'
+    }
+
+    if (MANAGED_METHODS.indexOf(method) >= 0) {
+      return await this.processJsonld(param)
+    }
+
+    throw ({ code: 404, message: 'Method not found' })
+  }
+
+  async checkOrigin({ origin }): Promise<boolean> {
+    if (this.whiteListedOrigins.has(origin)) return true
+    if (this.pendingRequests.has(origin)) return this.pendingRequests.get(origin)
+    const responsePromise = this.dialog.open(
+      ConfirmDialogComponent,
+      {
+        data: {
+          title: `Cross tab messaging`,
+          message: `${origin} would like to send data to interactive atlas viewer`,
+          okBtnText: `Allow`
+        }
+      }
+    ).afterClosed().toPromise()
+    this.pendingRequests.set(origin, responsePromise)
+    const response = await responsePromise
+    this.pendingRequests.delete(origin)
+    if (response) this.whiteListedOrigins.add(origin)
+    return response
+  }
+}
diff --git a/src/messaging/types.ts b/src/messaging/types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..eda7bc06a35931dd508ead545b9bb7f6df38c174
--- /dev/null
+++ b/src/messaging/types.ts
@@ -0,0 +1,62 @@
+import { InjectionToken } from "@angular/core";
+
+interface ILoadTemplateByIdPayload {
+  ['@id']: string
+}
+
+interface IResourceType {
+  swc: string
+}
+
+export type TVec4 = [number, number, number, number]
+export type TVec3 = [number, number, number]
+export type TMat3 = [TVec3, TVec3, TVec3]
+export type TMat4 = [TVec4, TVec4, TVec4, TVec4]
+
+interface ICommonResParam {
+  transform: TMat4
+}
+
+interface ILoadResource {
+  ['@id']: string
+  ['@type']: keyof IResourceType
+  url: string
+  unload: () => void
+  resourceParam: ICommonResParam
+}
+
+interface IUnloadResource {
+  ['@id']: string
+}
+
+interface ISetResp {
+  [key: string]: any
+}
+
+export interface IMessagingActionTmpl {
+  setResponse: ISetResp
+  loadTemplate: ILoadTemplateByIdPayload
+  loadResource: ILoadResource
+  unloadResource: IUnloadResource
+}
+
+export interface IMessagingActions<TAction extends keyof IMessagingActionTmpl> {
+  type: TAction
+  payload: IMessagingActionTmpl[TAction]
+}
+
+export interface ILoadMesh {
+  type: 'VTK'
+  id: string
+  url: string
+  customFragmentColor?: string
+}
+export const LOAD_MESH_TOKEN = new InjectionToken<(loadMeshParam: ILoadMesh) => void>('LOAD_MESH_TOKEN')
+
+export interface IWindowMessaging {
+  loadTempladById(payload: IMessagingActionTmpl['loadTemplate']): void
+  loadResource(payload: IMessagingActionTmpl['loadResource']): void
+  unloadResource(payload: IMessagingActionTmpl['unloadResource']): void
+}
+
+export const WINDOW_MESSAGING_HANDLER_TOKEN = new InjectionToken<IWindowMessaging>('WINDOW_MESSAGING_HANDLER_TOKEN')
diff --git a/src/messagingGlue.ts b/src/messagingGlue.ts
new file mode 100644
index 0000000000000000000000000000000000000000..443b58db56cfe20fcfcbe871232f6a11177977a4
--- /dev/null
+++ b/src/messagingGlue.ts
@@ -0,0 +1,111 @@
+import { OnDestroy } from "@angular/core";
+import { select, Store } from "@ngrx/store";
+import { IMessagingActionTmpl, IWindowMessaging } from "./messaging/types";
+import { ngViewerActionAddNgLayer, ngViewerActionRemoveNgLayer } from "./services/state/ngViewerState/actions";
+import { viewerStateSelectAtlas } from "./services/state/viewerState/actions";
+import { viewerStateFetchedAtlasesSelector } from "./services/state/viewerState/selectors";
+import { generalActionError } from "./services/stateStore.helper";
+
+export class MessagingGlue implements IWindowMessaging, OnDestroy {
+
+  private onDestroyCb: (() => void)[] = []
+  private tmplSpIdToAtlasId = new Map<string, string>()
+  private mapIdUnload = new Map<string, () => void>()
+
+  ngOnDestroy(){
+    while(this.onDestroyCb.length > 0) this.onDestroyCb.pop()()
+  }
+
+  constructor(private store: Store<any>){
+
+    const sub = this.store.pipe(
+      select(viewerStateFetchedAtlasesSelector)
+    ).subscribe((atlases: any[]) => {
+      for (const atlas of atlases) {
+        const { ['@id']: atlasId, templateSpaces } = atlas
+        for (const tmpl of templateSpaces) {
+          const { ['@id']: tmplId } = tmpl
+          this.tmplSpIdToAtlasId.set(tmplId, atlasId)
+        }
+      }
+    })
+
+    this.onDestroyCb.push(() => sub.unsubscribe())
+  }
+
+  /**
+   * it is important to not use select temlate by id. always go from the highest hierarchy,
+   * and enforce single direction flow when possible
+   */
+  loadTempladById( payload: IMessagingActionTmpl['loadTemplate'] ){
+    const atlasId = this.tmplSpIdToAtlasId.get(payload['@id'])
+    if (!atlasId) {
+      return this.store.dispatch(
+        generalActionError({
+          message: `atlas id with the corresponding templateId ${payload['@id']} not found.`
+        })
+      )
+    }
+    this.store.dispatch(
+      viewerStateSelectAtlas({
+        atlas: {
+          ['@id']: atlasId,
+          template: {
+            ['@id']: payload['@id']
+          }
+        }
+      })
+    )
+  }
+
+  loadResource(payload: IMessagingActionTmpl['loadResource']){
+    const {
+      unload,
+      url,
+      ["@type"]: type,
+      ["@id"]: swcLayerUuid,
+      resourceParam
+    } = payload
+
+    if (type === 'swc') {
+      const { transform } = resourceParam
+      const layer = {
+        name: swcLayerUuid,
+        id: swcLayerUuid,
+        source: `swc://${url}`,
+        mixability: 'mixable',
+        type: "segmentation",
+        "segments": [
+          "1"
+        ],
+        transform,
+      }
+
+      this.store.dispatch(
+        ngViewerActionAddNgLayer({
+          layer
+        })
+      )
+
+      this.mapIdUnload.set(swcLayerUuid, () => {
+        this.store.dispatch(
+          ngViewerActionRemoveNgLayer({
+            layer: {
+              name: swcLayerUuid
+            }
+          })
+        )
+        unload()
+      })
+    }
+  }
+  unloadResource(payload: IMessagingActionTmpl['unloadResource']) {
+    const { ["@id"]: id } = payload
+    const cb = this.mapIdUnload.get(id)
+    if (!cb) {
+      throw new Error(`Unload resource id ${id} does not exist.`)
+    }
+    cb()
+    this.mapIdUnload.delete(id)
+  }
+}
diff --git a/src/mouseoverModule/index.ts b/src/mouseoverModule/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6e9cd09486d0a2da56b1f28f86dce2cc6382d78d
--- /dev/null
+++ b/src/mouseoverModule/index.ts
@@ -0,0 +1,5 @@
+export { MouseOverIconPipe } from './mouseOverIcon.pipe'
+export { MouseOverTextPipe } from './mouseOverText.pipe'
+export { MouseHoverDirective } from './mouseover.directive'
+export { MouseoverModule } from './mouseover.module'
+export { TransformOnhoverSegmentPipe } from './transformOnhoverSegment.pipe'
\ No newline at end of file
diff --git a/src/mouseoverModule/mouseOverIcon.pipe.ts b/src/mouseoverModule/mouseOverIcon.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ba5f7686798d9417eb365580a84f16955cdf53d7
--- /dev/null
+++ b/src/mouseoverModule/mouseOverIcon.pipe.ts
@@ -0,0 +1,34 @@
+import { Pipe, PipeTransform } from "@angular/core"
+
+@Pipe({
+  name: 'mouseOverIconPipe',
+})
+
+export class MouseOverIconPipe implements PipeTransform {
+
+  public transform(type: string): {fontSet: string, fontIcon: string} {
+
+    switch (type) {
+    case 'landmark':
+      return {
+        fontSet: 'fas',
+        fontIcon: 'fa-map-marker-alt',
+      }
+    case 'segments':
+      return {
+        fontSet: 'fas',
+        fontIcon: 'fa-brain',
+      }
+    case 'userLandmark':
+      return {
+        fontSet: 'fas',
+        fontIcon: 'fa-map-marker-alt',
+      }
+    default:
+      return {
+        fontSet: 'fas',
+        fontIcon: 'fa-file',
+      }
+    }
+  }
+}
diff --git a/src/mouseoverModule/mouseOverText.pipe.ts b/src/mouseoverModule/mouseOverText.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5ba11425c0e78eb8dee14e9a25ba02a0ed0f9adf
--- /dev/null
+++ b/src/mouseoverModule/mouseOverText.pipe.ts
@@ -0,0 +1,54 @@
+import { Pipe, PipeTransform, SecurityContext } from "@angular/core"
+import { DomSanitizer, SafeHtml } from "@angular/platform-browser"
+import { TransformOnhoverSegmentPipe } from "./transformOnhoverSegment.pipe"
+
+@Pipe({
+  name: 'mouseOverTextPipe',
+})
+
+export class MouseOverTextPipe implements PipeTransform {
+
+  private transformOnHoverSegmentPipe: TransformOnhoverSegmentPipe
+  constructor(private sanitizer: DomSanitizer) {
+    this.transformOnHoverSegmentPipe = new TransformOnhoverSegmentPipe(this.sanitizer)
+  }
+
+  private renderText = ({ label, obj }): SafeHtml[] => {
+    switch (label) {
+    case 'landmark': {
+      const { dataset = [] } = obj
+      return [
+        this.sanitizer.sanitize(SecurityContext.HTML, obj.landmarkName),
+        ...(dataset.map(ds => this.sanitizer.bypassSecurityTrustHtml(`
+<span class="text-muted">
+  ${this.sanitizer.sanitize(SecurityContext.HTML, ds.name)}
+</span>
+`)))
+      ]
+    }
+    case 'segments':
+      return obj.map(({ segment }) => this.transformOnHoverSegmentPipe.transform(segment))
+    case 'userLandmark':
+      return [this.sanitizer.sanitize(SecurityContext.HTML, obj.name)]
+    default:
+      // ts-lint:disable-next-line
+      console.warn(`mouseOver.directive.ts#mouseOverTextPipe: Cannot be displayed: label: ${label}`)
+      return [this.sanitizer.bypassSecurityTrustHtml(`Cannot be displayed: label: ${label}`)]
+    }
+  }
+
+  public transform(inc: {segments: any, landmark: any, userLandmark: any}): Array<{label: string, text: SafeHtml[]}> {
+    const keys = Object.keys(inc)
+    return keys
+      // if is segments, filter out if lengtth === 0
+      .filter(key => Array.isArray(inc[key]) ? inc[key].length > 0 : true )
+      // for other properties, check if value is defined
+      .filter(key => !!inc[key])
+      .map(key => {
+        return {
+          label: key,
+          text: this.renderText({ label: key, obj: inc[key] })
+        }
+      })
+  }
+}
diff --git a/src/mouseoverModule/mouseover.directive.ts b/src/mouseoverModule/mouseover.directive.ts
new file mode 100644
index 0000000000000000000000000000000000000000..25f33743ce536d91150185cfab6c2e52e3764c8e
--- /dev/null
+++ b/src/mouseoverModule/mouseover.directive.ts
@@ -0,0 +1,127 @@
+import { Directive } from "@angular/core"
+import { select, Store } from "@ngrx/store"
+import { merge, Observable } from "rxjs"
+import { distinctUntilChanged, filter, map, scan, shareReplay, startWith, withLatestFrom } from "rxjs/operators"
+import { LoggingService } from "src/logging"
+import { uiStateMouseOverSegmentsSelector, uiStateMouseoverUserLandmark } from "src/services/state/uiState/selectors"
+import { viewerStateSelectedParcellationSelector } from "src/services/state/viewerState/selectors"
+import { deserialiseParcRegionId } from "common/util"
+import { temporalPositveScanFn } from "./util"
+
+@Directive({
+  selector: '[iav-mouse-hover]',
+  exportAs: 'iavMouseHover',
+})
+
+export class MouseHoverDirective {
+
+  public onHoverObs$: Observable<{segments: any, landmark: any, userLandmark: any}>
+  public currentOnHoverObs$: Observable<{segments: any, landmark: any, userLandmark: any}>
+
+  constructor(
+    private store$: Store<any>,
+    private log: LoggingService,
+  ) {
+
+    // TODO consider moving these into a single obs serviced by a DI service
+    // can potentially net better performance
+
+    const onHoverUserLandmark$ = this.store$.pipe(
+      select(uiStateMouseoverUserLandmark)
+    )
+
+    const onHoverLandmark$ = this.store$.pipe(
+      select('uiState'),
+      select('mouseOverLandmark'),
+    ).pipe(
+      map(landmark => {
+        if (landmark === null) { return landmark }
+        const idx = Number(landmark.replace('label=', ''))
+        if (isNaN(idx)) {
+          this.log.warn(`Landmark index could not be parsed as a number: ${landmark}`)
+          return {
+            landmarkName: idx,
+          }
+        } 
+      }),
+    )
+
+    const onHoverSegments$ = this.store$.pipe(
+      select(uiStateMouseOverSegmentsSelector),
+      filter(v => !!v),
+      withLatestFrom(
+        this.store$.pipe(
+          select(viewerStateSelectedParcellationSelector),
+          startWith(null),
+        ),
+      ),
+      map(([ arr, parcellationSelected ]) => parcellationSelected && parcellationSelected.auxillaryMeshIndices
+        ? arr.filter(({ segment }) => {
+          // if segment is not a string (i.e., not labelIndexId) return true
+          if (typeof segment !== 'string') { return true }
+          const { labelIndex } = deserialiseParcRegionId(segment)
+          return parcellationSelected.auxillaryMeshIndices.indexOf(labelIndex) < 0
+        })
+        : arr),
+      distinctUntilChanged((o, n) => o.length === n.length
+        && n.every(segment =>
+          o.find(oSegment => oSegment.layer.name === segment.layer.name
+            && oSegment.segment === segment.segment))),
+    )
+
+    const mergeObs = merge(
+      onHoverSegments$.pipe(
+        distinctUntilChanged(),
+        map(segments => {
+          return { segments }
+        }),
+      ),
+      onHoverLandmark$.pipe(
+        distinctUntilChanged(),
+        map(landmark => {
+          return { landmark }
+        }),
+      ),
+      onHoverUserLandmark$.pipe(
+        distinctUntilChanged(),
+        map(userLandmark => {
+          return { userLandmark }
+        }),
+      ),
+    ).pipe(
+      shareReplay(1),
+    )
+
+    this.onHoverObs$ = mergeObs.pipe(
+      scan((acc, curr) => {
+        return {
+          ...acc,
+          ...curr,
+        }
+      }, { segments: null, landmark: null, userLandmark: null }),
+      shareReplay(1),
+    )
+
+    this.currentOnHoverObs$ = mergeObs.pipe(
+      scan(temporalPositveScanFn, []),
+      map(arr => {
+
+        let returnObj = {
+          segments: null,
+          landmark: null,
+          userLandmark: null,
+        }
+
+        for (const val of arr) {
+          returnObj = {
+            ...returnObj,
+            ...val
+          }
+        }
+
+        return returnObj
+      }),
+      shareReplay(1),
+    )
+  }
+}
diff --git a/src/mouseoverModule/mouseover.module.ts b/src/mouseoverModule/mouseover.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..476fc900a2a6a3731b5bae0d7f6a2f60d836dd8a
--- /dev/null
+++ b/src/mouseoverModule/mouseover.module.ts
@@ -0,0 +1,26 @@
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { TransformOnhoverSegmentPipe } from "src/atlasViewer/onhoverSegment.pipe";
+import { MouseHoverDirective } from "./mouseover.directive";
+import { MouseOverIconPipe } from "./mouseOverIcon.pipe";
+import { MouseOverTextPipe } from "./mouseOverText.pipe";
+
+@NgModule({
+  imports: [
+    CommonModule,
+  ],
+  declarations: [
+    MouseHoverDirective,
+    MouseOverTextPipe,
+    TransformOnhoverSegmentPipe,
+    MouseOverIconPipe,
+  ],
+  exports: [
+    MouseHoverDirective,
+    MouseOverTextPipe,
+    TransformOnhoverSegmentPipe,
+    MouseOverIconPipe,
+  ]
+})
+
+export class MouseoverModule{}
\ No newline at end of file
diff --git a/src/mouseoverModule/transformOnhoverSegment.pipe.ts b/src/mouseoverModule/transformOnhoverSegment.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5199a582a1ba2e5084d7097996652a492211a342
--- /dev/null
+++ b/src/mouseoverModule/transformOnhoverSegment.pipe.ts
@@ -0,0 +1,29 @@
+import { Pipe, PipeTransform, SecurityContext } from "@angular/core";
+import { DomSanitizer, SafeHtml } from "@angular/platform-browser";
+
+@Pipe({
+  name: 'transformOnhoverSegment',
+})
+
+export class TransformOnhoverSegmentPipe implements PipeTransform {
+  constructor(private sanitizer: DomSanitizer) {
+
+  }
+
+  private sanitizeHtml(inc: string): SafeHtml {
+    return this.sanitizer.sanitize(SecurityContext.HTML, inc)
+  }
+
+  private getStatus(text: string) {
+    return ` <span class="text-muted">(${this.sanitizeHtml(text)})</span>`
+  }
+
+  public transform(segment: any | number): SafeHtml {
+    return this.sanitizer.bypassSecurityTrustHtml((
+      ( this.sanitizeHtml(segment.name) || segment) +
+      (segment.status
+        ? this.getStatus(segment.status)
+        : '')
+    ))
+  }
+}
diff --git a/src/atlasViewer/mouseOver.directive.spec.ts b/src/mouseoverModule/util.spec..ts
similarity index 94%
rename from src/atlasViewer/mouseOver.directive.spec.ts
rename to src/mouseoverModule/util.spec..ts
index 613d78ee825f93026770c06378d28e1b9464de9c..07453e0343eb31d5e6b1279b3a249a19b5301c2c 100644
--- a/src/atlasViewer/mouseOver.directive.spec.ts
+++ b/src/mouseoverModule/util.spec..ts
@@ -1,7 +1,7 @@
 import {} from 'jasmine'
 import { forkJoin, Subject } from 'rxjs';
 import { scan, skip, take } from 'rxjs/operators';
-import { temporalPositveScanFn } from './mouseOver.directive'
+import { temporalPositveScanFn } from './util'
 
 const segmentsPositive = { segments: [{ hello: 'world' }] } as {segments: any}
 const segmentsNegative = { segments: [] }
@@ -42,12 +42,12 @@ describe('temporalPositveScanFn', () => {
       take(1),
     )
 
-    forkJoin(
+    forkJoin([
       testFirstEv,
       testSecondEv,
       testThirdEv,
       testFourthEv,
-    ).pipe(
+    ]).pipe(
       take(1),
     ).subscribe(([ arr1, arr2, arr3, arr4 ]) => {
       expect(arr1).toEqual([ segmentsPositive ])
diff --git a/src/mouseoverModule/util.ts b/src/mouseoverModule/util.ts
new file mode 100644
index 0000000000000000000000000000000000000000..519202f8878440faf249ad3da41b163e5c66fd4b
--- /dev/null
+++ b/src/mouseoverModule/util.ts
@@ -0,0 +1,26 @@
+
+/**
+ * Scan function which prepends newest positive (i.e. defined) value
+ *
+ * e.g. const source = new Subject()
+ * source.pipe(
+ *  scan(temporalPositveScanFn, [])
+ * ).subscribe(this.log.log) // outputs
+ *
+ *
+ *
+ */
+export const temporalPositveScanFn = (acc: Array<{segments: any, landmark: any, userLandmark: any}>, curr: {segments: any, landmark: any, userLandmark: any}) => {
+
+  const keys = Object.keys(curr)
+
+  // empty array is truthy
+  const isPositive = keys.some(key => Array.isArray(curr[key])
+    ? curr[key].length > 0
+    : !!curr[key]
+  )
+
+  return isPositive
+    ? [curr, ...(acc.filter(item => !keys.some(key => !!item[key])))] as Array<{segments?: any, landmark?: any, userLandmark?: any}>
+    : acc.filter(item => !keys.some(key => !!item[key]))
+}
\ No newline at end of file
diff --git a/src/plugin/atlasViewer.pluginService.service.spec.ts b/src/plugin/atlasViewer.pluginService.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..918063217692a6b3033aa0065036d85c969b9c3e
--- /dev/null
+++ b/src/plugin/atlasViewer.pluginService.service.spec.ts
@@ -0,0 +1,322 @@
+import { CommonModule } from "@angular/common"
+import { HttpClientTestingModule, HttpTestingController } from "@angular/common/http/testing"
+import { NgModule } from "@angular/core"
+import { async, fakeAsync, TestBed, tick } from "@angular/core/testing"
+import { MockStore, provideMockStore } from "@ngrx/store/testing"
+import { ComponentsModule } from "src/components"
+import { DialogService } from "src/services/dialogService.service"
+import { selectorPluginCspPermission } from "src/services/state/userConfigState.helper"
+import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module"
+import { PureContantService } from "src/util"
+import { APPEND_SCRIPT_TOKEN, REMOVE_SCRIPT_TOKEN } from "src/util/constants"
+import { WidgetModule, WidgetServices } from "src/widget"
+import { PluginServices } from "./atlasViewer.pluginService.service"
+import { PluginUnit } from "./pluginUnit/pluginUnit.component"
+
+const MOCK_PLUGIN_MANIFEST = {
+  name: 'fzj.xg.MOCK_PLUGIN_MANIFEST',
+  templateURL: 'http://localhost:10001/template.html',
+  scriptURL: 'http://localhost:10001/script.js'
+}
+
+@NgModule({
+  declarations: [
+    PluginUnit,
+  ],
+  entryComponents: [
+    PluginUnit
+  ],
+  exports: [
+    PluginUnit
+  ]
+})
+
+class PluginUnitModule{}
+
+const spyfn = {
+  appendSrc: jasmine.createSpy('appendSrc')
+}
+
+
+
+describe('> atlasViewer.pluginService.service.ts', () => {
+  describe('> PluginServices', () => {
+    
+    let pluginService: PluginServices
+    let httpMock: HttpTestingController
+    let mockStore: MockStore
+    
+    beforeEach(async(() => {
+      TestBed.configureTestingModule({
+        imports: [
+          AngularMaterialModule,
+          CommonModule,
+          WidgetModule,
+          PluginUnitModule,
+          HttpClientTestingModule,
+          ComponentsModule,
+        ],
+        providers: [
+          provideMockStore(),
+          PluginServices,
+          {
+            provide: APPEND_SCRIPT_TOKEN,
+            useValue: spyfn.appendSrc
+          },
+          {
+            provide: REMOVE_SCRIPT_TOKEN,
+            useValue: () => Promise.resolve()
+          },
+          {
+            provide: DialogService,
+            useValue: {
+              getUserConfirm: () => Promise.resolve()
+            }
+          },
+          {
+            provide: PureContantService,
+            useValue: {
+              backendUrl: `http://localhost:3000/`
+            }
+          }
+        ]
+      }).compileComponents().then(() => {
+        
+        httpMock = TestBed.inject(HttpTestingController)
+        pluginService = TestBed.inject(PluginServices)
+        mockStore = TestBed.inject(MockStore)
+        pluginService.pluginViewContainerRef = {
+          createComponent: () => {
+            return {
+              onDestroy: () => {},
+              instance: {
+                elementRef: {
+                  nativeElement: {
+                    append: () => {}
+                  }
+                }
+              }
+            }
+          }
+        } as any
+
+        httpMock.expectOne('http://localhost:3000/plugins/manifests').flush('[]')
+
+        const widgetService = TestBed.inject(WidgetServices)
+        /**
+         * widget service floatingcontainer not inst in this circumstance
+         * TODO fix widget service tests importing widget service are not as flaky
+         */
+        widgetService.addNewWidget = () => {
+          return {} as any
+        }
+      })
+    }))
+
+    afterEach(() => {
+      spyfn.appendSrc.calls.reset()
+      const ctrl = TestBed.inject(HttpTestingController)
+      ctrl.verify()
+    })
+
+    it('> service can be inst', () => {
+      expect(pluginService).toBeTruthy()
+    })
+
+    it('expectOne is working as expected', done => {
+      
+      pluginService.fetch('test')
+        .then(text => {
+          expect(text).toEqual('bla')
+          done()
+        })
+      httpMock.expectOne('test').flush('bla')
+        
+    })
+
+    /**
+     * need to consider user confirmation on csp etc
+     */
+    describe('#launchPlugin', () => {
+
+      beforeEach(() => {
+        mockStore.overrideSelector(selectorPluginCspPermission, { value: false })
+      })
+
+      describe('> basic fetching functionality', () => {
+        it('> fetches templateURL and scriptURL properly', fakeAsync(() => {
+          
+          pluginService.launchPlugin({...MOCK_PLUGIN_MANIFEST})
+
+          tick(100)
+          
+          const mockTemplate = httpMock.expectOne(MOCK_PLUGIN_MANIFEST.templateURL)
+          mockTemplate.flush('hello world')
+          
+          tick(100)
+          
+          expect(spyfn.appendSrc).toHaveBeenCalledTimes(1)
+          expect(spyfn.appendSrc).toHaveBeenCalledWith(MOCK_PLUGIN_MANIFEST.scriptURL)
+          
+        }))
+
+        it('> template overrides templateURL', fakeAsync(() => {
+          pluginService.launchPlugin({
+            ...MOCK_PLUGIN_MANIFEST,
+            template: ''
+          })
+
+          tick(20)
+          httpMock.expectNone(MOCK_PLUGIN_MANIFEST.templateURL)
+        }))
+
+        it('> script with scriptURL throws', done => {
+          pluginService.launchPlugin({
+            ...MOCK_PLUGIN_MANIFEST,
+            script: '',
+            scriptURL: null
+          })
+            .then(() => {
+              /**
+               * should not pass
+               */
+              expect(true).toEqual(false)
+            })
+            .catch(e => {
+              done()
+            })
+          
+          /**
+           * http call will not be made, as rejection happens by Promise.reject, while fetch call probably happens at the next event cycle
+           */
+          httpMock.expectNone(MOCK_PLUGIN_MANIFEST.templateURL)
+        })
+      
+        describe('> user permission', () => {
+          let userConfirmSpy: jasmine.Spy
+          let readyPluginSpy: jasmine.Spy
+          let cspManifest = {
+            ...MOCK_PLUGIN_MANIFEST,
+            csp: {
+              'connect-src': [`'unsafe-eval'`]
+            }
+          }
+          afterEach(() => {
+            userConfirmSpy.calls.reset()
+            readyPluginSpy.calls.reset()
+          })
+          beforeEach(() => {
+            readyPluginSpy = spyOn(pluginService, 'readyPlugin').and.callFake(() => Promise.reject())
+            const dialogService = TestBed.inject(DialogService)
+            userConfirmSpy = spyOn(dialogService, 'getUserConfirm')
+          })
+
+          describe('> if user permission has been given', () => {
+            beforeEach(fakeAsync(() => {
+              mockStore.overrideSelector(selectorPluginCspPermission, { value: true })
+              userConfirmSpy.and.callFake(() => Promise.reject())
+              pluginService.launchPlugin({
+                ...cspManifest
+              }).catch(() => {
+                /**
+                 * expecting to throw because call fake returning promise.reject in beforeEach
+                 */
+              })
+              tick(20)
+            }))
+            it('> will not ask for permission', () => {
+              expect(userConfirmSpy).not.toHaveBeenCalled()
+            })
+
+            it('> will call ready plugin', () => {
+              expect(readyPluginSpy).toHaveBeenCalled()
+            })
+          })
+
+          describe('> if user permission has not yet been given', () => {
+            beforeEach(() => {
+              mockStore.overrideSelector(selectorPluginCspPermission, { value: false })
+            })
+            describe('> user permission', () => {
+              beforeEach(fakeAsync(() => {
+                pluginService.launchPlugin({
+                  ...cspManifest
+                }).catch(() => {
+                  /**
+                   * expecting to throw because call fake returning promise.reject in beforeEach
+                   */
+                })
+                tick(40)
+              }))
+              it('> will be asked for', () => {
+                expect(userConfirmSpy).toHaveBeenCalled()
+              })
+            })
+
+            describe('> if user accepts', () => {
+              beforeEach(fakeAsync(() => {
+                userConfirmSpy.and.callFake(() => Promise.resolve())
+
+                pluginService.launchPlugin({
+                  ...cspManifest
+                }).catch(() => {
+                  /**
+                   * expecting to throw because call fake returning promise.reject in beforeEach
+                   */
+                })
+              }))
+              it('> calls /POST user/pluginPermissions', () => {
+                httpMock.expectOne({
+                  method: 'POST',
+                  url: 'http://localhost:3000/user/pluginPermissions'
+                })
+              })
+            })
+
+            describe('> if user declines', () => {
+
+              beforeEach(fakeAsync(() => {
+                userConfirmSpy.and.callFake(() => Promise.reject())
+
+                pluginService.launchPlugin({
+                  ...cspManifest
+                }).catch(() => {
+                  /**
+                   * expecting to throw because call fake returning promise.reject in beforeEach
+                   */
+                })
+              }))
+              it('> calls /POST user/pluginPermissions', () => {
+                httpMock.expectNone({
+                  method: 'POST',
+                  url: 'http://localhost:3000/user/pluginPermissions'
+                })
+              })
+            })
+          })
+        })
+      })
+
+      describe('> racing slow connection when launching plugin', () => {
+        it('> when template/script has yet been fetched, repeated launchPlugin should not result in repeated fetching', fakeAsync(() => {
+
+          expect(pluginService.pluginIsLaunching(MOCK_PLUGIN_MANIFEST.name)).toBeFalsy()
+          expect(pluginService.pluginHasLaunched(MOCK_PLUGIN_MANIFEST.name)).toBeFalsy()
+          pluginService.launchPlugin({...MOCK_PLUGIN_MANIFEST})
+          pluginService.launchPlugin({...MOCK_PLUGIN_MANIFEST})
+          tick(20)
+          const req = httpMock.expectOne(MOCK_PLUGIN_MANIFEST.templateURL)
+          req.flush('baba')
+          tick(20)
+          expect(spyfn.appendSrc).toHaveBeenCalledTimes(1)
+
+          expect(
+            pluginService.pluginIsLaunching(MOCK_PLUGIN_MANIFEST.name) ||
+            pluginService.pluginHasLaunched(MOCK_PLUGIN_MANIFEST.name)
+          ).toBeTruthy()
+        }))
+      })
+    
+    })
+  })
+})
diff --git a/src/plugin/atlasViewer.pluginService.service.ts b/src/plugin/atlasViewer.pluginService.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..42ac1eb77d55de2adb541adf8c98e60e079ae199
--- /dev/null
+++ b/src/plugin/atlasViewer.pluginService.service.ts
@@ -0,0 +1,390 @@
+import { HttpClient } from '@angular/common/http'
+import { ComponentFactory, ComponentFactoryResolver, Injectable, ViewContainerRef, Inject, SecurityContext } from "@angular/core";
+import { PLUGINSTORE_ACTION_TYPES } from "src/services/state/pluginState.helper";
+import { PluginUnit } from "./pluginUnit/pluginUnit.component";
+import { select, Store } from "@ngrx/store";
+import { BehaviorSubject, from, merge, Observable, of } from "rxjs";
+import { catchError, filter, map, mapTo, shareReplay, switchMap, switchMapTo, take, tap } from "rxjs/operators";
+import { LoggingService } from 'src/logging';
+import { PluginHandler } from 'src/util/pluginHandler';
+import { WidgetUnit, WidgetServices } from "src/widget";
+import { APPEND_SCRIPT_TOKEN, REMOVE_SCRIPT_TOKEN, getHttpHeader } from 'src/util/constants';
+import { PluginFactoryDirective } from './pluginFactory.directive';
+import { selectorPluginCspPermission } from 'src/services/state/userConfigState.helper';
+import { DialogService } from 'src/services/dialogService.service';
+import { DomSanitizer } from '@angular/platform-browser';
+import { MatSnackBar } from '@angular/material/snack-bar';
+import { PureContantService } from 'src/util';
+
+const requiresReloadMd = `\n\n***\n\n**warning**: interactive atlas viewer **will** be reloaded in order for the change to take effect.`
+
+export const registerPluginFactoryDirectiveFactory = (pSer: PluginServices) => {
+  return (pFactoryDirective: PluginFactoryDirective) => {
+    pSer.loadExternalLibraries = pFactoryDirective.loadExternalLibraries.bind(pFactoryDirective)
+    pSer.unloadExternalLibraries = pFactoryDirective.unloadExternalLibraries.bind(pFactoryDirective)
+    pSer.pluginViewContainerRef = pFactoryDirective.viewContainerRef
+  }
+}
+
+@Injectable({
+  providedIn : 'root',
+})
+
+export class PluginServices {
+
+  public pluginHandlersMap: Map<string, PluginHandler> = new Map()
+
+  public loadExternalLibraries: (libraries: string[]) => Promise<any> = () => Promise.reject(`fail to overwritten`)
+  public unloadExternalLibraries: (libraries: string[]) => void = () => { throw new Error(`failed to be overwritten`) }
+
+  public fetchedPluginManifests: IPluginManifest[] = []
+  public pluginViewContainerRef: ViewContainerRef
+
+  private pluginUnitFactory: ComponentFactory<PluginUnit>
+  public minimisedPlugins$: Observable<Set<string>>
+
+  /**
+   * TODO remove polyfil and convert all calls to this.fetch to http client
+   */
+  public fetch: (url: string, httpOption?: any) => Promise<any> = (url, httpOption = {}) => this.http.get(url, httpOption).toPromise()
+
+  constructor(
+    private widgetService: WidgetServices,
+    private cfr: ComponentFactoryResolver,
+    private store: Store<any>,
+    private dialogService: DialogService,
+    private snackbar: MatSnackBar,
+    private http: HttpClient,
+    private log: LoggingService,
+    private sanitizer: DomSanitizer,
+    private constantSvc: PureContantService,
+    @Inject(APPEND_SCRIPT_TOKEN) private appendSrc: (src: string) => Promise<HTMLScriptElement>,
+    @Inject(REMOVE_SCRIPT_TOKEN) private removeSrc: (src: HTMLScriptElement) => void,
+  ) {
+
+    this.pluginUnitFactory = this.cfr.resolveComponentFactory( PluginUnit )
+
+    /**
+     * TODO convert to rxjs streams, instead of Promise.all
+     */
+    const pluginManifestsUrl = `${this.constantSvc.backendUrl}plugins/manifests`
+
+    this.http.get<IPluginManifest[]>(pluginManifestsUrl, {
+      responseType: 'json',
+      headers: getHttpHeader(),
+    }).subscribe(
+      arr => this.fetchedPluginManifests = arr,
+      this.log.error,
+    )
+
+    this.minimisedPlugins$ = merge(
+      of(new Set()),
+      this.widgetService.minimisedWindow$,
+    ).pipe(
+      map(set => {
+        const returnSet = new Set<string>()
+        for (const [pluginName, wu] of this.mapPluginNameToWidgetUnit) {
+          if (set.has(wu)) {
+            returnSet.add(pluginName)
+          }
+        }
+        return returnSet
+      }),
+      shareReplay(1),
+    )
+
+    this.launchedPlugins$ = new BehaviorSubject(new Set())
+  }
+
+  public launchNewWidget = (manifest) => this.launchPlugin(manifest)
+    .then(handler => {
+      this.orphanPlugins.add(manifest)
+      handler.onShutdown(() => {
+        this.orphanPlugins.delete(manifest)
+      })
+    })
+
+  public readyPlugin(plugin: IPluginManifest): Promise<any> {
+    const isDefined = input => typeof input !== 'undefined' && input !== null
+    if (!isDefined(plugin.scriptURL)) {
+      return Promise.reject(`inline script has been deprecated. use scriptURL instead`)
+    }
+    if (isDefined(plugin.template)) {
+      return Promise.resolve()
+    }
+    if (plugin.templateURL) {
+      return this.fetch(plugin.templateURL, {responseType: 'text'})
+        .then(template => {
+          plugin.template = template
+        })
+    }
+    return Promise.reject('both template and templateURL are not defined')
+  }
+
+  private launchedPlugins: Set<string> = new Set()
+  public launchedPlugins$: BehaviorSubject<Set<string>>
+  public pluginHasLaunched(pluginName: string) {
+    return this.launchedPlugins.has(pluginName)
+  }
+  public addPluginToLaunchedSet(pluginName: string) {
+    this.launchedPlugins.add(pluginName)
+    this.launchedPlugins$.next(this.launchedPlugins)
+  }
+  public removePluginFromLaunchedSet(pluginName: string) {
+    this.launchedPlugins.delete(pluginName)
+    this.launchedPlugins$.next(this.launchedPlugins)
+  }
+
+  public pluginIsLaunching(pluginName: string) {
+    return this.launchingPlugins.has(pluginName)
+  }
+  public addPluginToIsLaunchingSet(pluginName: string) {
+    this.launchingPlugins.add(pluginName)
+  }
+  public removePluginFromIsLaunchingSet(pluginName: string) {
+    this.launchingPlugins.delete(pluginName)
+  }
+
+  private mapPluginNameToWidgetUnit: Map<string, WidgetUnit> = new Map()
+
+  public pluginIsMinimised(pluginName: string) {
+    return this.widgetService.isMinimised( this.mapPluginNameToWidgetUnit.get(pluginName) )
+  }
+
+  private launchingPlugins: Set<string> = new Set()
+  public orphanPlugins: Set<IPluginManifest> = new Set()
+
+  public async revokePluginPermission(pluginKey: string) {
+    const createRevokeMd = (pluginKey: string) => `You are about to revoke the permission given to ${pluginKey}.${requiresReloadMd}`
+
+    try {
+      await this.dialogService.getUserConfirm({
+        markdown: createRevokeMd(pluginKey)
+      })
+
+      this.http.delete(
+        `${this.constantSvc.backendUrl}user/pluginPermissions/${encodeURIComponent(pluginKey)}`, 
+        {
+          headers: getHttpHeader()
+        }
+      ).subscribe(
+        () => {
+          window.location.reload()
+        },
+        err => {
+          this.snackbar.open(`Error revoking plugin permission ${err.toString()}`, 'Dismiss')
+        }
+      )
+    } catch (_e) {
+      /**
+       * user cancelled workflow
+       */
+    }
+  }
+
+  public async launchPlugin(plugin: IPluginManifest): Promise<PluginHandler> {
+    if (this.pluginIsLaunching(plugin.name)) {
+      // plugin launching please be patient
+      // TODO add visual feedback
+      return
+    }
+    if ( this.pluginHasLaunched(plugin.name)) {
+      // plugin launched
+      // TODO add visual feedback
+
+      // if widget window is minimized, maximize it
+
+      const wu = this.mapPluginNameToWidgetUnit.get(plugin.name)
+      if (this.widgetService.isMinimised(wu)) {
+        this.widgetService.unminimise(wu)
+      } else {
+        this.widgetService.minimise(wu)
+      }
+      return
+    }
+
+    this.addPluginToIsLaunchingSet(plugin.name)
+
+    const { csp, displayName, name = '', version = 'latest' } = plugin
+    const pluginKey = `${name}::${version}`
+    const createPermissionMd = ({ csp, name, version }) => {
+      const sanitize = val =>  this.sanitizer.sanitize(SecurityContext.HTML, val)
+      const getCspRow = ({ key }) => {
+        return `| ${sanitize(key)} | ${csp[key].map(v => '`' + sanitize(v) + '`').join(',')} |`
+      }
+      return `**${sanitize(displayName || name)}** version **${sanitize(version)}** requires additional permission from you to run:\n\n| permission | detail |\n| --- | --- |\n${Object.keys(csp).map(key => getCspRow({ key })).join('\n')}${requiresReloadMd}`
+    } 
+
+    await new Promise((rs, rj) => {
+      this.store.pipe(
+        select(selectorPluginCspPermission, { key: pluginKey }),
+        take(1),
+        switchMap(userAgreed => {
+          if (userAgreed.value) return of(true)
+
+          /**
+           * check if csp exists
+           */
+          if (!csp || Object.keys(csp).length === 0) {
+            return of(true)
+          }
+          /**
+           * TODO: check do not ask status
+           */
+          return from(
+            this.dialogService.getUserConfirm({
+              markdown: createPermissionMd({ csp, name, version })
+            })
+          ).pipe(
+            mapTo(true),
+            catchError(() => of(false)),
+            filter(v => !!v),
+            switchMapTo(
+              this.http.post(`${this.constantSvc.backendUrl}user/pluginPermissions`, 
+                { [pluginKey]: csp },
+                {
+                  responseType: 'json',
+                  headers: getHttpHeader()
+                })
+            ),
+            tap(() => {
+              window.location.reload()
+            }),
+            mapTo(false)
+          )
+        }),
+        take(1),
+      ).subscribe(
+        val => val ? rs(null) : rj(`val is falsy`),
+        err => rj(err)
+      )
+    })
+
+    await this.readyPlugin(plugin)
+
+    /**
+     * catch when pluginViewContainerRef as not been overwritten?
+     */
+    if (!this.pluginViewContainerRef) {
+      throw new Error(`pluginViewContainerRef not populated`)
+    }
+    const pluginUnit = this.pluginViewContainerRef.createComponent( this.pluginUnitFactory )
+    /* TODO in v0.2, I used:
+
+    const template = document.createElement('div')
+    template.insertAdjacentHTML('afterbegin',template)
+
+    // reason was:
+    // changed from innerHTML to insertadjacenthtml to accomodate angular elements ... not too sure about the actual ramification
+
+    */
+
+    const handler = new PluginHandler()
+    this.pluginHandlersMap.set(plugin.name, handler)
+
+    /**
+     * define the handler properties prior to appending plugin script
+     * so that plugin script can access properties w/o timeout
+     */
+    handler.initState = plugin.initState
+      ? plugin.initState
+      : null
+
+    handler.initStateUrl = plugin.initStateUrl
+      ? plugin.initStateUrl
+      : null
+
+    handler.setInitManifestUrl = (url) => this.store.dispatch({
+      type : PLUGINSTORE_ACTION_TYPES.SET_INIT_PLUGIN,
+      manifest : {
+        name : plugin.name,
+        initManifestUrl : url,
+      },
+    })
+
+    const shutdownCB = [
+      () => {
+        this.removePluginFromLaunchedSet(plugin.name)
+      },
+    ]
+
+    handler.onShutdown = (cb) => {
+      if (typeof cb !== 'function') {
+        this.log.warn('onShutdown requires the argument to be a function')
+        return
+      }
+      shutdownCB.push(cb)
+    }
+
+    const scriptEl = await this.appendSrc(plugin.scriptURL)
+
+    handler.onShutdown(() => this.removeSrc(scriptEl))
+
+    const template = document.createElement('div')
+    template.insertAdjacentHTML('afterbegin', plugin.template)
+    pluginUnit.instance.elementRef.nativeElement.append( template )
+
+    const widgetCompRef = this.widgetService.addNewWidget(pluginUnit, {
+      state : 'floating',
+      exitable : true,
+      persistency: plugin.persistency,
+      title : plugin.displayName || plugin.name,
+    })
+
+    this.addPluginToLaunchedSet(plugin.name)
+    this.removePluginFromIsLaunchingSet(plugin.name)
+
+    this.mapPluginNameToWidgetUnit.set(plugin.name, widgetCompRef.instance)
+
+    const unsubscribeOnPluginDestroy = []
+
+    // TODO deprecate sec
+    handler.blink = (_sec?: number) => {
+      widgetCompRef.instance.blinkOn = true
+    }
+
+    handler.setProgressIndicator = (val) => widgetCompRef.instance.progressIndicator = val
+
+    handler.shutdown = () => {
+      widgetCompRef.instance.exit()
+    }
+
+    handler.onShutdown(() => {
+      unsubscribeOnPluginDestroy.forEach(s => s.unsubscribe())
+      this.pluginHandlersMap.delete(plugin.name)
+      this.mapPluginNameToWidgetUnit.delete(plugin.name)
+    })
+
+    pluginUnit.onDestroy(() => {
+      while (shutdownCB.length > 0) {
+        shutdownCB.pop()()
+      }
+    })
+
+    return handler
+  }
+}
+
+export interface IPluginManifest {
+  name?: string
+  version?: string
+  displayName?: string
+  templateURL?: string
+  template?: string
+  scriptURL?: string
+  script?: string
+  initState?: any
+  initStateUrl?: string
+  persistency?: boolean
+
+  description?: string
+  desc?: string
+
+  homepage?: string
+  authors?: string
+
+  csp?: {
+    'connect-src'?: string[]
+    'script-src'?: string[]
+  }
+}
diff --git a/src/plugin/index.ts b/src/plugin/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..bae5875b5d19c8b096188c6c8074e42a2ff14674
--- /dev/null
+++ b/src/plugin/index.ts
@@ -0,0 +1,9 @@
+export {
+  IPluginManifest,
+  PluginServices,
+  registerPluginFactoryDirectiveFactory,
+} from './atlasViewer.pluginService.service'
+
+export {
+  PluginModule
+} from './plugin.module'
\ No newline at end of file
diff --git a/src/atlasViewer/pluginUnit/plugin.module.ts b/src/plugin/plugin.module.ts
similarity index 54%
rename from src/atlasViewer/pluginUnit/plugin.module.ts
rename to src/plugin/plugin.module.ts
index 12763521faa0539bdf7eb38d7b275eba8263dcb7..5ba71ea0297eb761cc9a9fb71ecdeaaf6864ee3e 100644
--- a/src/atlasViewer/pluginUnit/plugin.module.ts
+++ b/src/plugin/plugin.module.ts
@@ -1,27 +1,36 @@
+import { CommonModule, DOCUMENT } from "@angular/common";
 import { NgModule } from "@angular/core";
-import { PluginUnit } from "./pluginUnit.component";
+import { LoggingModule } from "src/logging";
+import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module";
+import { UtilModule } from "src/util";
+import { appendScriptFactory, APPEND_SCRIPT_TOKEN, removeScriptFactory, REMOVE_SCRIPT_TOKEN } from "src/util/constants";
 import { PluginServices, registerPluginFactoryDirectiveFactory } from "./atlasViewer.pluginService.service";
+import { PluginBannerUI } from "./pluginBanner/pluginBanner.component";
+import { PluginCspCtrlCmp } from "./pluginCsp/pluginCsp.component";
 import { PluginFactoryDirective, REGISTER_PLUGIN_FACTORY_DIRECTIVE } from "./pluginFactory.directive";
-import { LoggingModule } from "src/logging";
-import { APPEND_SCRIPT_TOKEN, appendScriptFactory, REMOVE_SCRIPT_TOKEN, removeScriptFactory } from "src/util/constants";
-import { DOCUMENT } from "@angular/common";
+import { PluginUnit } from "./pluginUnit/pluginUnit.component";
 
 @NgModule({
-  imports:[
+  imports: [
+    CommonModule,
     LoggingModule,
+    UtilModule,
+    AngularMaterialModule,
   ],
   declarations: [
+    PluginCspCtrlCmp,
     PluginUnit,
-    PluginFactoryDirective
-  ],
-  entryComponents: [
-    PluginUnit
+    PluginFactoryDirective,
+    PluginBannerUI,
   ],
   exports: [
+    PluginCspCtrlCmp,
+    PluginBannerUI,
     PluginUnit,
-    PluginFactoryDirective
+    PluginFactoryDirective,
   ],
   providers: [
+
     PluginServices,
     {
       provide: REGISTER_PLUGIN_FACTORY_DIRECTIVE,
@@ -40,5 +49,4 @@ import { DOCUMENT } from "@angular/common";
     },
   ]
 })
-
 export class PluginModule{}
\ No newline at end of file
diff --git a/src/ui/pluginBanner/pluginBanner.component.ts b/src/plugin/pluginBanner/pluginBanner.component.ts
similarity index 90%
rename from src/ui/pluginBanner/pluginBanner.component.ts
rename to src/plugin/pluginBanner/pluginBanner.component.ts
index c154f1b666a4aa1ba19cd4568eef5d91394f1888..59de1360d44b7db00fc1f9e5567371debf03b650 100644
--- a/src/ui/pluginBanner/pluginBanner.component.ts
+++ b/src/plugin/pluginBanner/pluginBanner.component.ts
@@ -1,5 +1,5 @@
 import { Component, ViewChild, TemplateRef } from "@angular/core";
-import { IPluginManifest, PluginServices } from "src/atlasViewer/pluginUnit";
+import { IPluginManifest, PluginServices } from "../atlasViewer.pluginService.service";
 import { MatDialog } from "@angular/material/dialog";
 
 @Component({
diff --git a/src/ui/pluginBanner/pluginBanner.style.css b/src/plugin/pluginBanner/pluginBanner.style.css
similarity index 100%
rename from src/ui/pluginBanner/pluginBanner.style.css
rename to src/plugin/pluginBanner/pluginBanner.style.css
diff --git a/src/ui/pluginBanner/pluginBanner.template.html b/src/plugin/pluginBanner/pluginBanner.template.html
similarity index 100%
rename from src/ui/pluginBanner/pluginBanner.template.html
rename to src/plugin/pluginBanner/pluginBanner.template.html
diff --git a/src/plugin/pluginCsp/pluginCsp.component.ts b/src/plugin/pluginCsp/pluginCsp.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7ff3c8a1fcfcc7faa96237f9cbb9ad539104812c
--- /dev/null
+++ b/src/plugin/pluginCsp/pluginCsp.component.ts
@@ -0,0 +1,32 @@
+import { Component } from "@angular/core";
+import { select, Store } from "@ngrx/store";
+import { map } from "rxjs/operators";
+import { PluginServices } from "../atlasViewer.pluginService.service";
+import { selectorAllPluginsCspPermission } from "src/services/state/userConfigState.store";
+
+@Component({
+  selector: 'plugin-csp-controller',
+  templateUrl: './pluginCsp.template.html',
+  styleUrls: [
+    './pluginCsp.style.css'
+  ]
+})
+
+export class PluginCspCtrlCmp{
+
+  public pluginCsp$ = this.store$.pipe(
+    select(selectorAllPluginsCspPermission),
+    map(pluginCsp => Object.keys(pluginCsp).map(key => ({ pluginKey: key, pluginCsp: pluginCsp[key] }))),
+  )
+
+  constructor(
+    private store$: Store<any>,
+    private pluginService: PluginServices,
+  ){
+
+  }
+
+  revoke(pluginKey: string){
+    this.pluginService.revokePluginPermission(pluginKey)
+  }
+}
\ No newline at end of file
diff --git a/src/plugin/pluginCsp/pluginCsp.style.css b/src/plugin/pluginCsp/pluginCsp.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/plugin/pluginCsp/pluginCsp.template.html b/src/plugin/pluginCsp/pluginCsp.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..477d41a9cff6964a477b21df24e11231604c8910
--- /dev/null
+++ b/src/plugin/pluginCsp/pluginCsp.template.html
@@ -0,0 +1,52 @@
+
+<ng-container *ngIf="pluginCsp$ | async as pluginsCsp; else fallbackTmpl">
+  
+  <ng-template #pluginsCspContainerTmpl>
+    <ng-container *ngTemplateOutlet="pluginCpTmpl; context: { pluginsCsp: pluginsCsp }">
+    </ng-container>  
+  </ng-template>
+  
+  <ng-container *ngIf="pluginsCsp.length === 0; else pluginsCspContainerTmpl">
+    <ng-container *ngTemplateOutlet="fallbackTmpl">
+    </ng-container>
+  </ng-container>
+</ng-container>
+
+<ng-template #fallbackTmpl>
+  You have not granted permission to any plugins.
+</ng-template>
+
+<ng-template #pluginCpTmpl let-pluginsCsp="pluginsCsp">
+  <p>
+    You have granted permission to the following plugins
+  </p>
+  
+  <mat-accordion>
+    <mat-expansion-panel *ngFor="let pluginCsp of pluginCsp$ | async">
+      <mat-expansion-panel-header>
+        <mat-panel-title>
+          {{ pluginCsp['pluginKey'] }}
+        </mat-panel-title>
+      </mat-expansion-panel-header>
+  
+      <button mat-raised-button
+        color="warn"
+        (click)="revoke(pluginCsp['pluginKey'])">
+        Revoke
+      </button>
+  
+      <mat-list>
+        <ng-container *ngFor="let csp of pluginCsp['pluginCsp'] | objToArray">
+          <span mat-subheader>
+            {{ csp['key'] }}
+          </span>
+          <mat-list-item *ngFor="let item of csp['value']">
+            {{ item }}
+          </mat-list-item>
+        </ng-container>
+      </mat-list>
+  
+    </mat-expansion-panel>
+  </mat-accordion>
+  
+</ng-template>
diff --git a/src/atlasViewer/pluginUnit/pluginFactory.directive.spec.ts b/src/plugin/pluginFactory.directive.spec.ts
similarity index 100%
rename from src/atlasViewer/pluginUnit/pluginFactory.directive.spec.ts
rename to src/plugin/pluginFactory.directive.spec.ts
diff --git a/src/atlasViewer/pluginUnit/pluginFactory.directive.ts b/src/plugin/pluginFactory.directive.ts
similarity index 100%
rename from src/atlasViewer/pluginUnit/pluginFactory.directive.ts
rename to src/plugin/pluginFactory.directive.ts
diff --git a/src/atlasViewer/pluginUnit/pluginUnit.component.ts b/src/plugin/pluginUnit/pluginUnit.component.ts
similarity index 100%
rename from src/atlasViewer/pluginUnit/pluginUnit.component.ts
rename to src/plugin/pluginUnit/pluginUnit.component.ts
diff --git a/src/plugin/pluginUnit/pluginUnit.template.html b/src/plugin/pluginUnit/pluginUnit.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/plugin_examples/migrationGuide.md b/src/plugin_examples/migrationGuide.md
index 1489d5b848ab70382b63575190309020b0f8c148..fcd5e040b9333b262dbbac60b6d3237859fe7f1c 100644
--- a/src/plugin_examples/migrationGuide.md
+++ b/src/plugin_examples/migrationGuide.md
@@ -34,7 +34,6 @@ Plugin APIs have changed drastically from v0.1.0 to v0.2.0. Here is a list of pl
   - *showAllSegments()* : Function that selects all segments.
   - *hideAllSegments()* : Function that deselects all segments.
   - *loadLayer(layerObject)* : Function that loads a custom neuroglancer compatible layer into the viewer (e.g. precomputed, NIFTI, etc). Does not influence UI. 
-  - ~~*reapplyNehubaMeshFix()* Function that reapplies the cosmetic change to NehubaViewer (such as custom colour map, if defined)~~ removed. use **applyColourMap(colourMap)** instead
   - *mouseEvent* RxJs Observable. Read more at [rxjs doc](http://reactivex.io/rxjs/)
     - *mouseEvent.filter(filterFn:({eventName : String, event: Event})=>boolean)* returns an Observable. Filters the event stream according to the filter function.
     - *mouseEvent.map(mapFn:({eventName : String, event: Event})=>any)* returns an Observable. Map the event stream according to the map function.
@@ -49,16 +48,4 @@ Plugin APIs have changed drastically from v0.1.0 to v0.2.0. Here is a list of pl
   - ~~*onParcellationSelection(callback)* : Function that attach a callback function to user selecting a different parcellation~~ removed. use **window.interactiveViewer.metadata.selectedParcellationBSubject** instead.
   - ~~*afterParcellationSelection(callback)* : Function that attach a callback function to be called after the parcellation selection process is complete and *selectedParcellation* is updated.~~ removed
   - *modalControl*
-    - *getModalHandler()* : Function returning a handler to change/show/hide/listen to a Modal. 
-    - *modalHander* methods:
-      - *hide()* : Dynamically hides the modal
-      - *show()* : Shows the modal
-      - *onHide(callback(reason)=>void)* : Attaches an onHide callback. 
-      - *onHidden(callback(reason)=>void)* : Attaches an onHidden callback. 
-      - *onShow(callback(reason)=>void)* : Attaches an onShow callback. 
-      - *onShown(callback(reason)=>void)* : Attaches an onShown callback.
-    - *modalHandler* properties:
-      - title : title of the modal (String)
-      - body : body of the modal shown (JSON, Array, String)
-      - footer : footer of the modal (String)
-      - config : config of the modal
\ No newline at end of file
+    - ~~*getModalHandler()* : Function returning a handler to change/show/hide/listen to a Modal.~~ removed
\ No newline at end of file
diff --git a/src/plugin_examples/plugin_api.md b/src/plugin_examples/plugin_api.md
index a8b29b30f48e239a0e2a1294c845eaa24abc0e8d..6954f15b52d7900e14a493376423c61697dacb82 100644
--- a/src/plugin_examples/plugin_api.md
+++ b/src/plugin_examples/plugin_api.md
@@ -26,6 +26,8 @@ Map of layer name to Map of labelIndex (used by neuroglancer and nehuba) to the
 
 ### viewerHandle
 
+> **nb** `viewerHandle` may be undefined at any time (user be yet to select an atlas, user could have unloaded an atlas. ...etc)
+
 #### setNavigationLoc(coordinates, realspace?:boolean)
 
 Function that teleports the navigation state to coordinates : [x:number,y:number,z:number]. Optional arg determine if the set of coordinates is in realspace (default) or voxelspace.
@@ -222,34 +224,6 @@ whether the modal is dismissable on click backdrop/esc key (Boolean)
 
 *n.b. if true, users will not be able to interact with the viewer unless you specifically call `handler.hide()`*
 
-#### getToastHandler()
-
-returns a toastHandler objectm, which has the following methods/properties:
-
-##### show()
-
-Show the toast
-
-##### hide()
-
-Dynamically hides the toast
-
-##### message
-
-message on the toast
-
-##### htmlMessage
-
-HTML message. If used to display user content, beware of script injection. Angular strips `style` attribute, so use `class` and bootstrap for styling.
-
-##### dismissable
-
-allow user dismiss the toast via x 
-
-##### timeout
-
-auto hide (in ms). set to 0 for not auto hide.
-
 #### launchNewWidget(manifest)
 
 returns a Promise. expects a JSON object, with the same key value as a plugin manifest. the *name* key must be unique, or the promise will be rejected. 
diff --git a/src/res/css/extra_styles.css b/src/res/css/extra_styles.css
index 081fa25ea672331dbdb2509fcaf98e891028bbf8..4db5bf3001a3386c61b72c36ba8190fd974e921e 100644
--- a/src/res/css/extra_styles.css
+++ b/src/res/css/extra_styles.css
@@ -176,6 +176,11 @@ markdown-dom pre code
   white-space:pre;
 }
 
+markdown-dom p
+{
+  margin: 0;
+}
+
 .highlight
 {
   background-color:rgba(150,150,0,0.5);
@@ -298,6 +303,10 @@ markdown-dom pre code
   width: 0!important;
 }
 
+.w-3em
+{
+  width: 3em!important;
+}
 .w-5em
 {
   width: 5em!important;
@@ -361,6 +370,11 @@ markdown-dom pre code
   pointer-events: all;
 }
 
+.t-a-ease-200
+{
+  transition: all ease 200ms;
+}
+
 .t-a-ease-500
 {
   transition: all ease 500ms;
@@ -381,6 +395,16 @@ markdown-dom pre code
   height: 2rem!important;
 }
 
+.h-4rem
+{
+  height: 4rem!important;
+}
+
+.h-50vh
+{
+  height: 50vh!important;
+}
+
 .h-5em
 {
   height: 5em!important;
@@ -433,6 +457,11 @@ markdown-dom pre code
   opacity: 0.5!important;
 }
 
+.muted-3
+{
+  opacity: 0.3!important;
+}
+
 .card
 {
   background:none;
@@ -501,11 +530,11 @@ markdown-dom pre code
 }
 
 .outline-none {
-    outline: none;
+  outline: none;
 }
 
 .cursor-pointer {
-    cursor: pointer!important;
+  cursor: pointer!important;
 }
 
 .z-index-1
@@ -819,3 +848,25 @@ mat-list.sm mat-list-item
 {
   color: inherit!important;
 }
+
+.sidenav-cover-header-container
+{
+  padding: 16px;
+  margin: -16px!important;
+  padding-top: 6rem;
+}
+
+.no-padding-dialog > mat-dialog-container
+{
+  padding-top:0 !important;
+  padding-right:0 !important;
+  padding-left:0 !important;
+}
+.h-50px
+{
+  height:50px;
+}
+.scale-80
+{
+  transform: scale(0.8);
+}
diff --git a/src/res/ext/MNI152.json b/src/res/ext/MNI152.json
index c20593fc588bbefd0b80de33340c624f217362c0..967ff0c943444f348219fa9509f3ec30d3ffdf65 100644
--- a/src/res/ext/MNI152.json
+++ b/src/res/ext/MNI152.json
@@ -7,16 +7,30 @@
   "species": "Human",
   "useTheme": "dark",
   "ngId": "mni152",
+  "auxMeshes": [{
+    "name": "ICBM152 2009c nonlinear asymmetric - Cortex",
+    "displayName": "Cortex",
+    "@id": "icbm2009c_nonlin_asym_cortex",
+    "ngId": "auxMesh",
+    "labelIndicies": [ 65500, 65510 ],
+    "rgb": [255, 255, 255],
+    "visible": true
+  },{
+    "name": "ICBM152 2009c nonlinear asymmetric - Sulci",
+    "displayName": "Sulci",
+    "@id": "icbm2009c_nonlin_asym_sulci",
+    "ngId": "auxMesh",
+    "labelIndicies": [ 65501, 65511 ],
+    "rgb": [255, 255, 255],
+    "visible": false
+  }],
   "nehubaConfigURL": "nehubaConfig/MNI152NehubaConfig",
   "parcellations": [
     {
-      "fullId": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-25",
-      "@id": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-25",
+      "fullId": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-26",
+      "@id": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-26",
       "name": "Cytoarchitectonic Maps - v2.5.1",
       "displayName": "Cytoarchitectonic Maps",
-      "auxillaryMeshIndices": [
-        65535
-      ],
       "hasAdditionalViewMode": [
         "connectivity"
       ],
@@ -13144,9 +13158,6 @@
     },{
       "@id": "juelich/iav/atlas/v1.0.0/79cbeaa4ee96d5d3dfe2876e9f74b3dc3d3ffb84304fb9b965b1776563a1069c",
       "ngId": "superficial-white-bundle-HCP",
-      "auxillaryMeshIndices": [
-        65535
-      ],
       "name": "Short Fiber Bundles - HCP",
       "originDatasets": [
         {
@@ -34996,9 +35007,6 @@
       "fullId": "juelich/iav/atlas/v1.0.0/8",
       "name": "Cytoarchitectonic maps - v1.18",
       "ngId": "jubrain mni152 v18 left",
-      "auxillaryMeshIndices": [
-        65535
-      ],
       "hasAdditionalViewMode": [
         "connectivity"
       ],
@@ -43100,9 +43108,6 @@
     {
       "@id": "juelich/iav/atlas/v1.0.0/5",
       "ngId": "fibre bundle long",
-      "auxillaryMeshIndices": [
-        65535
-      ],
       "type": "parcellation",
       "surfaceParcellation": true,
       "ngData": null,
@@ -46358,9 +46363,6 @@
       ],
       "@id": "juelich/iav/atlas/v1.0.0/6",
       "ngId": "fibre bundle short",
-      "auxillaryMeshIndices": [
-        65535
-      ],
       "type": "parcellation",
       "surfaceParcellation": true,
       "ngData": null,
@@ -46379,9 +46381,6 @@
       "name": "DiFuMo Atlas (64 dimensions)",
       "ngId": "DiFuMo Atlas (64 dimensions)",
       "@id": "minds/core/parcellationatlas/v1.0.0/d80fbab2-ce7f-4901-a3a2-3c8ef8a3b721",
-      "auxillaryMeshIndices": [
-        65535
-      ],
       "originDatasets": [
         {
           "kgSchema": "minds/core/dataset/v1.0.0",
@@ -47547,9 +47546,6 @@
       "name": "DiFuMo Atlas (128 dimensions)",
       "ngId": "DiFuMo Atlas (128 dimensions)",
       "@id": "minds/core/parcellationatlas/v1.0.0/73f41e04-b7ee-4301-a828-4b298ad05ab8",
-      "auxillaryMeshIndices": [
-        65535
-      ],
       "originDatasets": [
         {
           "kgSchema": "minds/core/dataset/v1.0.0",
@@ -49867,9 +49863,6 @@
       "name": "DiFuMo Atlas (256 dimensions)",
       "ngId": "DiFuMo Atlas (256 dimensions)",
       "@id": "minds/core/parcellationatlas/v1.0.0/141d510f-0342-4f94-ace7-c97d5f160235",
-      "auxillaryMeshIndices": [
-        65535
-      ],
       "originDatasets": [
         {
           "kgSchema": "minds/core/dataset/v1.0.0",
@@ -54491,9 +54484,6 @@
       "name": "DiFuMo Atlas (512 dimensions)",
       "ngId": "DiFuMo Atlas (512 dimensions)",
       "@id": "minds/core/parcellationatlas/v1.0.0/63b5794f-79a4-4464-8dc1-b32e170f3d16",
-      "auxillaryMeshIndices": [
-        65535
-      ],
       "originDatasets": [
         {
           "kgSchema": "minds/core/dataset/v1.0.0",
@@ -63723,9 +63713,6 @@
       "name": "DiFuMo Atlas (1024 dimensions)",
       "ngId": "DiFuMo Atlas (1024 dimensions)",
       "@id": "minds/core/parcellationatlas/v1.0.0/12fca5c5-b02c-46ce-ab9f-f12babf4c7e1",
-      "auxillaryMeshIndices": [
-        65535
-      ],
       "originDatasets": [
         {
           "kgSchema": "minds/core/dataset/v1.0.0",
diff --git a/src/res/ext/MNI152NehubaConfig.json b/src/res/ext/MNI152NehubaConfig.json
index 32f89fb2eceeec97268024776561287a36761fc5..ead7dfe5211e81dde3478e27f9e3cbdd9f95e038 100644
--- a/src/res/ext/MNI152NehubaConfig.json
+++ b/src/res/ext/MNI152NehubaConfig.json
@@ -93,6 +93,37 @@
             ]
           ]
         },
+        "auxMesh": {
+          "type": "segmentation",
+          "visible": true,
+          "source": "precompmesh://https://neuroglancer.humanbrainproject.eu/precomputed/icbm152_2009c_nonlin_asym",
+          "transform": [
+            [
+              1,
+              0,
+              0,
+              -96500000
+            ],
+            [
+              0,
+              1,
+              0,
+              -132500000
+            ],
+            [
+              0,
+              0,
+              1,
+              -78500000
+            ],
+            [
+              0,
+              0,
+              0,
+              1
+            ]
+          ]
+        },
         "jubrain mni152 v18 left": {
           "type": "segmentation",
           "visible": false,
@@ -220,7 +251,7 @@
         "MNI152_V25_RIGHT_NG_SPLIT_HEMISPHERE": {
           "type": "segmentation",
           "visible": true,
-          "source": "precomputed://https://neuroglancer-dev.humanbrainproject.eu/precomputed/data-repo-ng-bot/JulichBrainV2_5_3/precomputed/JulichBrain_MPMAtlas_r_N10_nlin2Stdicbm152asym2009c_publicDOI_453c4c95827e38dcce5372b200065cba",
+          "source": "precomputed://https://neuroglancer.humanbrainproject.eu/precomputed/data-repo-ng-bot/JulichBrainV2_5_3/precomputed/JulichBrain_MPMAtlas_r_N10_nlin2Stdicbm152asym2009c_publicDOI_453c4c95827e38dcce5372b200065cba",
           "transform": [
             [
               1,
@@ -251,7 +282,7 @@
         "MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE": {
           "type": "segmentation",
           "visible": true,
-          "source": "precomputed://https://neuroglancer-dev.humanbrainproject.eu//precomputed/data-repo-ng-bot/JulichBrainV2_5_3/precomputed/JulichBrain_MPMAtlas_l_N10_nlin2Stdicbm152asym2009c_publicDOI_3f5ec6016bc2242769c41befdbc1b2e0",
+          "source": "precomputed://https://neuroglancer.humanbrainproject.eu//precomputed/data-repo-ng-bot/JulichBrainV2_5_3/precomputed/JulichBrain_MPMAtlas_l_N10_nlin2Stdicbm152asym2009c_publicDOI_3f5ec6016bc2242769c41befdbc1b2e0",
           "transform": [
             [
               1,
diff --git a/src/res/ext/allenMouse.json b/src/res/ext/allenMouse.json
index 9c9a5ec855bb7c5bf3a9da191f287ff7100219ce..67432c0726200b55204b591dfb83c2b1798134de 100644
--- a/src/res/ext/allenMouse.json
+++ b/src/res/ext/allenMouse.json
@@ -9,6 +9,15 @@
     "nissl"
   ],
   "useTheme": "dark",
+  "auxMeshes": [{
+    "name": "Allen CCF v3 whole brain",
+    "displayName": "Whole brain",
+    "@id": "allen_ccf_v3_wholebrain",
+    "ngId": "auxMesh",
+    "labelIndicies": [ 997 ],
+    "rgb": [255, 255, 255],
+    "visible": true
+  }],
   "nehubaConfigURL": "nehubaConfig/allenMouseNehubaConfig",
   "parcellations": [
     {
@@ -18,6 +27,9 @@
       "name": "Allen Mouse Common Coordinate Framework v3 2017",
       "ngData": null,
       "type": "parcellation",
+      "auxillaryMeshIndices": [
+        997
+      ],
       "regions": [
         {
           "ngId": "v3_2017",
@@ -19372,6 +19384,9 @@
       "name": "Allen Mouse Common Coordinate Framework v3 2015",
       "ngData": null,
       "type": "parcellation",
+      "auxillaryMeshIndices": [
+        997
+      ],
       "regions": [
         {
           "ngId": "atlas",
@@ -19387,6 +19402,7 @@
           ],
           "name": "root",
           "labelIndex": 997,
+          "unselectable": true,
           "relatedAreas": [
             {
               "name": "Whole brain",
diff --git a/src/res/ext/allenMouseNehubaConfig.json b/src/res/ext/allenMouseNehubaConfig.json
index ee86f8d398edc3dfda2d5c3dd4622447b7f77c9b..817703930fa97632f3f25963dcae7134bbb4fc8d 100644
--- a/src/res/ext/allenMouseNehubaConfig.json
+++ b/src/res/ext/allenMouseNehubaConfig.json
@@ -115,7 +115,37 @@
         },
         "v3_2017": {
           "type": "segmentation",
-          "source": "precomputed://https://neuroglancer-dev.humanbrainproject.eu/precomputed/AMBA/parcellations/v3_2017",
+          "source": "precomputed://https://neuroglancer.humanbrainproject.eu/precomputed/AMBA/parcellations/v3_2017",
+          "transform": [
+            [
+              0,
+              0,
+              -1,
+              5670000
+            ],
+            [
+              -1,
+              0,
+              0,
+              6570000
+            ],
+            [
+              0,
+              -1,
+              0,
+              3970000
+            ],
+            [
+              0,
+              0,
+              0,
+              1
+            ]
+          ]
+        },
+        "auxMesh": {
+          "type": "segmentation",
+          "source": "precompmesh://https://neuroglancer.humanbrainproject.eu/precomputed/AMBA/parcellations/v3_2017_mesh",
           "transform": [
             [
               0,
diff --git a/src/res/ext/atlas/atlas_multiLevelHuman.json b/src/res/ext/atlas/atlas_multiLevelHuman.json
index 0f163667bc0a03d822061302bbca99b3b0ba93c5..d249b0fc87ded3b44b4db65559c688d6f5fef8a0 100644
--- a/src/res/ext/atlas/atlas_multiLevelHuman.json
+++ b/src/res/ext/atlas/atlas_multiLevelHuman.json
@@ -9,7 +9,7 @@
       "displayName": "ICBM 152 2009c Nonlinear Asymmetric",
       "availableIn": [
         {
-          "@id": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-25",
+          "@id": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-26",
           "name": "Cytoarchitectonic maps - v2.5"
         },
         {
@@ -68,6 +68,15 @@
           "@id": "juelich/iav/atlas/v1.0.0/4"
         }
       ]
+    },
+    {
+      "@id": "julich/tmp/referencespace/freesurfer",
+      "name": "Freesurfer atlas test tmpl",
+      "availableIn": [
+        {
+          "@id": "julich/tmp/parcellation/freesurfer-test-parc"
+        }
+      ]
     }
   ],
   "parcellations": [
@@ -76,7 +85,7 @@
       "name": "Cytoarchitectonic Maps - v1.18",
       "baseLayer": true,
       "@version": {
-        "@next": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-25",
+        "@next": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-26",
         "@this": "juelich/iav/atlas/v1.0.0/8",
         "name": "v1.18",
         "@previous": null
@@ -109,12 +118,12 @@
       ]
     },
     {
-      "@id": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-25",
+      "@id": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-26",
       "name": "Cytoarchitectonic maps",
       "baseLayer": true,
       "@version": {
         "@next": null,
-        "@this": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-25",
+        "@this": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-26",
         "name": "v2.6",
         "@previous": "juelich/iav/atlas/v1.0.0/8"
       },
@@ -260,6 +269,15 @@
           }]
         }
       ]
+    },
+    {
+      "@id": "julich/tmp/parcellation/freesurfer-test-parc",
+      "name": "Freesurfer atlas parc test",
+      "availableIn": [
+        {
+          "@id": "julich/tmp/referencespace/freesurfer"
+        }
+      ]
     }
   ]
 }
diff --git a/src/res/ext/bigbrain.json b/src/res/ext/bigbrain.json
index 0857719d508189afea49a81dd7fa07ef7df9ac2d..88dabdd6cc129d69209922aa395397d5e48c038d 100644
--- a/src/res/ext/bigbrain.json
+++ b/src/res/ext/bigbrain.json
@@ -1393,6 +1393,23 @@
       "type": "parcellation",
       "ngData": null,
       "ngId": " tissue type: ",
+      "auxMeshes": [{
+        "name": "Big Brain - white matter",
+        "displayName": "White matter",
+        "@id": "bigbrain_whitematter",
+        "ngId": "auxMesh",
+        "labelIndicies": [ 200 ],
+        "rgb": [255, 255, 255],
+        "visible": false
+      },{
+        "name": "Big Brain - grey matter",
+        "displayName": "Grey matter",
+        "@id": "bigbrain_greymatter",
+        "ngId": "auxMesh",
+        "labelIndicies": [ 100 ],
+        "rgb": [255, 255, 255],
+        "visible": true
+      }],
       "regions": [
         {
           "name": "Isocortex",
diff --git a/src/res/ext/bigbrainNehubaConfig.json b/src/res/ext/bigbrainNehubaConfig.json
index 04142e6ad796f0c67dfc6d7df3e65a63a5d3c164..cbb71f4d7fd241a8fae67a750a225bab813c4129 100644
--- a/src/res/ext/bigbrainNehubaConfig.json
+++ b/src/res/ext/bigbrainNehubaConfig.json
@@ -93,9 +93,40 @@
             ]
           ]
         },
+        "auxMesh": {
+          "type": "segmentation",
+          "visible": false,
+          "source": "precompmesh://https://neuroglancer.humanbrainproject.org/precomputed/BigBrainRelease.2015/classif_mesh",
+          "transform": [
+            [
+              1,
+              0,
+              0,
+              -70666600
+            ],
+            [
+              0,
+              1,
+              0,
+              -72910000
+            ],
+            [
+              0,
+              0,
+              1,
+              -58777700
+            ],
+            [
+              0,
+              0,
+              0,
+              1
+            ]
+          ]
+        },
         "v1": {
           "type": "segmentation",
-          "source": "precomputed://https://neuroglancer-dev.humanbrainproject.org/precomputed/BigBrainRelease.2015/2019_05_01_v1",
+          "source": "precomputed://https://neuroglancer.humanbrainproject.org/precomputed/BigBrainRelease.2015/2019_05_01_v1",
           "segments": [
             "0"
           ],
@@ -130,7 +161,7 @@
         },
         "v2": {
           "type": "segmentation",
-          "source": "precomputed://https://neuroglancer-dev.humanbrainproject.org/precomputed/BigBrainRelease.2015/2019_05_01_v2",
+          "source": "precomputed://https://neuroglancer.humanbrainproject.org/precomputed/BigBrainRelease.2015/2019_05_01_v2",
           "segments": [
             "0"
           ],
@@ -165,7 +196,7 @@
         },
         "v3v": {
           "type": "segmentation",
-          "source": "precomputed://https://neuroglancer-dev.humanbrainproject.org/precomputed/BigBrainRelease.2015/2020_09_29_v3v",
+          "source": "precomputed://https://neuroglancer.humanbrainproject.org/precomputed/BigBrainRelease.2015/2020_09_29_v3v",
           "segments": [
             "0"
           ],
@@ -200,7 +231,7 @@
         },
         "v5": {
           "type": "segmentation",
-          "source": "precomputed://https://neuroglancer-dev.humanbrainproject.org/precomputed/BigBrainRelease.2015/2020_09_29_v5",
+          "source": "precomputed://https://neuroglancer.humanbrainproject.org/precomputed/BigBrainRelease.2015/2020_09_29_v5",
           "segments": [
             "0"
           ],
@@ -235,7 +266,7 @@
         },
         "interpolated": {
           "type": "segmentation",
-          "source": "precomputed://https://neuroglancer-dev.humanbrainproject.org/precomputed/BigBrainRelease.2015/2019_05_22_interpolated_areas",
+          "source": "precomputed://https://neuroglancer.humanbrainproject.org/precomputed/BigBrainRelease.2015/2019_05_22_interpolated_areas",
           "segments": [
             "0"
           ],
@@ -270,7 +301,7 @@
         },
         "LGB-lam1": {
           "type": "segmentation",
-          "source": "precomputed://https://neuroglancer-dev.humanbrainproject.org/precomputed/BigBrainRelease.2015/2020_11_11_LGB-lam/LGB-lam1/",
+          "source": "precomputed://https://neuroglancer.humanbrainproject.org/precomputed/BigBrainRelease.2015/2020_11_11_LGB-lam/LGB-lam1/",
           "segments": [
             "0"
           ],
@@ -305,7 +336,7 @@
         },
         "LGB-lam2": {
           "type": "segmentation",
-          "source": "precomputed://https://neuroglancer-dev.humanbrainproject.org/precomputed/BigBrainRelease.2015/2020_11_11_LGB-lam/LGB-lam2/",
+          "source": "precomputed://https://neuroglancer.humanbrainproject.org/precomputed/BigBrainRelease.2015/2020_11_11_LGB-lam/LGB-lam2/",
           "segments": [
             "0"
           ],
@@ -340,7 +371,7 @@
         },
         "LGB-lam3": {
           "type": "segmentation",
-          "source": "precomputed://https://neuroglancer-dev.humanbrainproject.org/precomputed/BigBrainRelease.2015/2020_11_11_LGB-lam/LGB-lam3/",
+          "source": "precomputed://https://neuroglancer.humanbrainproject.org/precomputed/BigBrainRelease.2015/2020_11_11_LGB-lam/LGB-lam3/",
           "segments": [
             "0"
           ],
@@ -375,7 +406,7 @@
         },
         "LGB-lam4": {
           "type": "segmentation",
-          "source": "precomputed://https://neuroglancer-dev.humanbrainproject.org/precomputed/BigBrainRelease.2015/2020_11_11_LGB-lam/LGB-lam4/",
+          "source": "precomputed://https://neuroglancer.humanbrainproject.org/precomputed/BigBrainRelease.2015/2020_11_11_LGB-lam/LGB-lam4/",
           "segments": [
             "0"
           ],
@@ -410,7 +441,7 @@
         },
         "LGB-lam5": {
           "type": "segmentation",
-          "source": "precomputed://https://neuroglancer-dev.humanbrainproject.org/precomputed/BigBrainRelease.2015/2020_11_11_LGB-lam/LGB-lam5/",
+          "source": "precomputed://https://neuroglancer.humanbrainproject.org/precomputed/BigBrainRelease.2015/2020_11_11_LGB-lam/LGB-lam5/",
           "segments": [
             "0"
           ],
@@ -445,7 +476,112 @@
         },
         "LGB-lam6": {
           "type": "segmentation",
-          "source": "precomputed://https://neuroglancer-dev.humanbrainproject.org/precomputed/BigBrainRelease.2015/2020_11_11_LGB-lam/LGB-lam6/",
+          "source": "precomputed://https://neuroglancer.humanbrainproject.org/precomputed/BigBrainRelease.2015/2020_11_11_LGB-lam/LGB-lam6/",
+          "segments": [
+            "0"
+          ],
+          "selectedAlpha": 0.45,
+          "notSelectedAlpha": 0,
+          "transform": [
+            [
+              1,
+              0,
+              0,
+              -70677184.0
+            ],
+            [
+              0,
+              1,
+              0,
+              -7290000.0
+            ],
+            [
+              0,
+              0,
+              1,
+              -58788284.0
+            ],
+            [
+              0,
+              0,
+              0,
+              1
+            ]
+          ]
+        },
+        "MGBd": {
+          "type": "segmentation",
+          "source": "precomputed://https://neuroglancer.humanbrainproject.org/precomputed/BigBrainRelease.2015/2021_04_27_mgb/2021_04_27_MGBd/",
+          "segments": [
+            "0"
+          ],
+          "selectedAlpha": 0.45,
+          "notSelectedAlpha": 0,
+          "transform": [
+            [
+              1,
+              0,
+              0,
+              -70677184.0
+            ],
+            [
+              0,
+              1,
+              0,
+              -7290000.0
+            ],
+            [
+              0,
+              0,
+              1,
+              -58788284.0
+            ],
+            [
+              0,
+              0,
+              0,
+              1
+            ]
+          ]
+        },
+        "MGBm": {
+          "type": "segmentation",
+          "source": "precomputed://https://neuroglancer.humanbrainproject.org/precomputed/BigBrainRelease.2015/2021_04_27_mgb/2021_04_27_MGBm/",
+          "segments": [
+            "0"
+          ],
+          "selectedAlpha": 0.45,
+          "notSelectedAlpha": 0,
+          "transform": [
+            [
+              1,
+              0,
+              0,
+              -70677184.0
+            ],
+            [
+              0,
+              1,
+              0,
+              -7290000.0
+            ],
+            [
+              0,
+              0,
+              1,
+              -58788284.0
+            ],
+            [
+              0,
+              0,
+              0,
+              1
+            ]
+          ]
+        },
+        "MGBv": {
+          "type": "segmentation",
+          "source": "precomputed://https://neuroglancer.humanbrainproject.org/precomputed/BigBrainRelease.2015/2021_04_27_mgb/2021_04_27_MGBv/",
           "segments": [
             "0"
           ],
@@ -585,7 +721,7 @@
         },
         "cortical layers": {
           "type": "segmentation",
-          "source": "precomputed://https://neuroglancer-dev.humanbrainproject.org/precomputed/BigBrainRelease.2015/2019_05_27_cortical_layers",
+          "source": "precomputed://https://neuroglancer.humanbrainproject.org/precomputed/BigBrainRelease.2015/2019_05_27_cortical_layers",
           "selectedAlpha": 0.5,
           "notSelectedAlpha": 0,
           "transform": [
diff --git a/src/res/ext/colin.json b/src/res/ext/colin.json
index 2c1d0cd2639a41c661fb6e45c8f695e84299ee33..372a589b763d8141dd444b752b833a8814227c87 100644
--- a/src/res/ext/colin.json
+++ b/src/res/ext/colin.json
@@ -9,8 +9,8 @@
   "nehubaConfigURL": "nehubaConfig/colinNehubaConfig",
   "parcellations": [
     {
-      "fullId": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-25",
-      "@id": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-25",
+      "fullId": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-26",
+      "@id": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-26",
       "name": "Cytoarchitectonic Maps - v2.5.1",
       "displayName": "Cytoarchitectonic Maps",
       "auxillaryMeshIndices": [
diff --git a/src/res/ext/freesurfer.json b/src/res/ext/freesurfer.json
new file mode 100644
index 0000000000000000000000000000000000000000..caf6816aa78a641f61e045240312e5c0a1d4d61a
--- /dev/null
+++ b/src/res/ext/freesurfer.json
@@ -0,0 +1,5288 @@
+{
+  "name": "Freesurfer Test Tmpl",
+  "@id": "julich/tmp/referencespace/freesurfer",
+  "@type": "julich/tmp/referencespace",
+  "useTheme": "dark",
+  "three-surfer": {
+    "@context": {
+      "rootUrl": "https://neuroglancer.humanbrainproject.eu/precomputed/freesurfer/20210305",
+      "rootUrl2": "https://neuroglancer-dev.humanbrainproject.eu/precomputed/data-repo/20210415-new-freesurfer",
+      "fsa_fsa": "/fsaverage/fsaverage",
+      "fsa_fsa6": "/fsaverage/fsaverage6",
+      "label_fsa_lh": "https://neuroglancer.humanbrainproject.eu/precomputed/freesurfer/20210305/julichbrain_labels/lh.JulichBrain_MPMAtlas_l_N10_nlin2Stdicbm152asym2009c_publicDOI_83fb39b2811305777db0eb80a0fc8b53.allSub_RF_ANTs_MNI152_orig_to_fsaverage.gii",
+      "label_fsa_rh": "https://neuroglancer.humanbrainproject.eu/precomputed/freesurfer/20210305/julichbrain_labels/rh.JulichBrain_MPMAtlas_r_N10_nlin2Stdicbm152asym2009c_publicDOI_172e93a5bec140c111ac862268f0d046.allSub_RF_ANTs_MNI152_orig_to_fsaverage.gii",
+      "label_fsa6_lh": "https://neuroglancer.humanbrainproject.eu/precomputed/freesurfer/20210305/julichbrain_labels/lh.JulichBrain_MPMAtlas_l_N10_nlin2Stdicbm152asym2009c_publicDOI_83fb39b2811305777db0eb80a0fc8b53.BV_MNI152_orig_to_fsaverage6.gii",
+      "label_fsa6_rh": "https://neuroglancer.humanbrainproject.eu/precomputed/freesurfer/20210305/julichbrain_labels/rh.JulichBrain_MPMAtlas_r_N10_nlin2Stdicbm152asym2009c_publicDOI_172e93a5bec140c111ac862268f0d046.BV_MNI152_orig_to_fsaverage6.gii",
+      "label_bv_hcp32k_lh": "https://neuroglancer.humanbrainproject.eu/precomputed/freesurfer/20210305/julichbrain_labels/lh.JulichBrain_MPMAtlas_l_N10_nlin2Stdicbm152asym2009c_publicDOI_83fb39b2811305777db0eb80a0fc8b53.allSub_RF_ANTs_MNI152_orig_to_hcp32k.gii",
+      "label_bv_hcp32k_rh": "https://neuroglancer.humanbrainproject.eu/precomputed/freesurfer/20210305/julichbrain_labels/rh.JulichBrain_MPMAtlas_r_N10_nlin2Stdicbm152asym2009c_publicDOI_172e93a5bec140c111ac862268f0d046.allSub_RF_ANTs_MNI152_orig_to_hcp32k.gii",
+      "label_ants_hcp32k_lh": "https://neuroglancer.humanbrainproject.eu/precomputed/freesurfer/20210305/julichbrain_labels/lh.JulichBrain_MPMAtlas_l_N10_nlin2Stdicbm152asym2009c_publicDOI_83fb39b2811305777db0eb80a0fc8b53.BV_MNI152_orig_to_hcp32k.gii",
+      "label_ants_hcp32k_rh": "https://neuroglancer.humanbrainproject.eu/precomputed/freesurfer/20210305/julichbrain_labels/rh.JulichBrain_MPMAtlas_r_N10_nlin2Stdicbm152asym2009c_publicDOI_172e93a5bec140c111ac862268f0d046.BV_MNI152_orig_to_hcp32k.gii",
+      "label_fsaverage6_cleaned_lh":"/lh.JulichBrain_MPMAtlas_l_N10_nlin2Stdicbm152asym2009c_publicDOI_83fb39b2811305777db0eb80a0fc8b53.BV_MNI152_orig_to_fsaverage6_cleaned.gii",
+      "label_fsaverage_cleaned_lh":"/lh.JulichBrain_MPMAtlas_l_N10_nlin2Stdicbm152asym2009c_publicDOI_83fb39b2811305777db0eb80a0fc8b53.BV_MNI152_orig_to_fsaverage_cleaned.gii",
+      "label_hcp32k_cleaned_lh":"/lh.JulichBrain_MPMAtlas_l_N10_nlin2Stdicbm152asym2009c_publicDOI_83fb39b2811305777db0eb80a0fc8b53.BV_MNI152_orig_to_hcp32k_cleaned.gii",
+      "label_fsaverage6_cleaned_rh":"/rh.JulichBrain_MPMAtlas_r_N10_nlin2Stdicbm152asym2009c_publicDOI_172e93a5bec140c111ac862268f0d046.BV_MNI152_orig_to_fsaverage6_cleaned.gii",
+      "label_fsaverage_cleaned_rh":"/rh.JulichBrain_MPMAtlas_r_N10_nlin2Stdicbm152asym2009c_publicDOI_172e93a5bec140c111ac862268f0d046.BV_MNI152_orig_to_fsaverage_cleaned.gii",
+      "label_hcp32k_cleaned_rh":"/rh.JulichBrain_MPMAtlas_r_N10_nlin2Stdicbm152asym2009c_publicDOI_172e93a5bec140c111ac862268f0d046.BV_MNI152_orig_to_hcp32k_cleaned.gii"
+    },
+    "modes": [
+      {
+        "name": "cleaned/fsaverage6/white",
+        "meshes": [
+          {
+            "mesh": "rootUrl:fsa_fsa6:/lh.white.gii",
+            "colormap": "rootUrl2:label_fsaverage6_cleaned_lh:",
+            "hemisphere": "left"
+          },
+          {
+            "mesh": "rootUrl:fsa_fsa6:/rh.white.gii",
+            "colormap": "rootUrl2:label_fsaverage6_cleaned_rh:",
+            "hemisphere": "right"
+          }
+        ]
+      },
+      {
+        "name": "cleaned/fsaverage/white",
+        "meshes": [
+          {
+            "mesh": "rootUrl:fsa_fsa:/lh.white.gii",
+            "colormap": "rootUrl2:label_fsaverage_cleaned_lh:",
+            "hemisphere": "left"
+          },
+          {
+            "mesh": "rootUrl:fsa_fsa:/rh.white.gii",
+            "colormap": "rootUrl2:label_fsaverage_cleaned_rh:",
+            "hemisphere": "right"
+          }
+        ]
+      },
+      {
+        "name": "cleaned/fsaverage6/pial",
+        "meshes": [
+          {
+            "mesh": "rootUrl:fsa_fsa6:/lh.pial.gii",
+            "colormap": "rootUrl2:label_fsaverage6_cleaned_lh:",
+            "hemisphere": "left"
+          },
+          {
+            "mesh": "rootUrl:fsa_fsa6:/rh.pial.gii",
+            "colormap": "rootUrl2:label_fsaverage6_cleaned_rh:",
+            "hemisphere": "right"
+          }
+        ]
+      },
+      {
+        "name": "cleaned/fsaverage/pial",
+        "meshes": [
+          {
+            "mesh": "rootUrl:fsa_fsa:/lh.pial.gii",
+            "colormap": "rootUrl2:label_fsaverage_cleaned_lh:",
+            "hemisphere": "left"
+          },
+          {
+            "mesh": "rootUrl:fsa_fsa:/rh.pial.gii",
+            "colormap": "rootUrl2:label_fsaverage_cleaned_rh:",
+            "hemisphere": "right"
+          }
+        ]
+      },
+      {
+        "name": "cleaned/fsaverage6/inflated",
+        "meshes": [
+          {
+            "mesh": "rootUrl2:/fsa6-translated-inflated/lh.inflated.surf.gii",
+            "colormap": "rootUrl2:label_fsaverage6_cleaned_lh:",
+            "hemisphere": "left"
+          },
+          {
+            "mesh": "rootUrl2:/fsa6-translated-inflated/rh.inflated.surf.gii",
+            "colormap": "rootUrl2:label_fsaverage6_cleaned_rh:",
+            "hemisphere": "right"
+          }
+        ]
+      },
+      {
+        "name": "cleaned/fsaverage/inflated",
+        "meshes": [
+          {
+            "mesh": "rootUrl2:/fsa7-translated-inflated/lh.inflated.surf.gii",
+            "colormap": "rootUrl2:label_fsaverage_cleaned_lh:",
+            "hemisphere": "left"
+          },
+          {
+            "mesh": "rootUrl2:/fsa7-translated-inflated/rh.inflated.surf.gii",
+            "colormap": "rootUrl2:label_fsaverage_cleaned_rh:",
+            "hemisphere": "right"
+          }
+        ]
+      },
+      {
+        "name": "fsaverage/fsaverage/pial",
+        "meshes": [
+          {
+            "mesh": "rootUrl:fsa_fsa:/lh.pial.gii",
+            "colormap": "label_fsa_lh:",
+            "hemisphere": "left"
+          },
+          {
+            "mesh": "rootUrl:fsa_fsa:/rh.pial.gii",
+            "colormap": "label_fsa_rh:",
+            "hemisphere": "right"
+          }
+        ]
+      },
+      {
+        "name": "fsaverage/fsaverage/white",
+        "meshes": [
+          {
+            "mesh": "rootUrl:fsa_fsa:/lh.white.gii",
+            "colormap": "label_fsa_lh:",
+            "hemisphere": "left"
+          },
+          {
+            "mesh": "rootUrl:fsa_fsa:/rh.white.gii",
+            "colormap": "label_fsa_rh:",
+            "hemisphere": "right"
+          }
+        ]
+      },
+      {
+        "name": "fsaverage/fsaverage/inflated",
+        "meshes": [
+          {
+            "mesh": "rootUrl2:/fsa7-translated-inflated/lh.inflated.surf.gii",
+            "colormap": "label_fsa_lh:",
+            "hemisphere": "left"
+          },
+          {
+            "mesh": "rootUrl2:/fsa7-translated-inflated/rh.inflated.surf.gii",
+            "colormap": "label_fsa_rh:",
+            "hemisphere": "right"
+          }
+        ]
+      },
+      {
+        "name": "fsaverage/fsaverage6/pial",
+        "meshes": [
+          {
+            "mesh": "rootUrl:fsa_fsa6:/lh.pial.gii",
+            "colormap": "label_fsa6_lh:",
+            "hemisphere": "left"
+          },
+          {
+            "mesh": "rootUrl:fsa_fsa6:/rh.pial.gii",
+            "colormap": "label_fsa6_rh:",
+            "hemisphere": "right"
+          }
+        ]
+      },
+      {
+        "name": "fsaverage/fsaverage6/white",
+        "meshes": [
+          {
+            "mesh": "rootUrl:fsa_fsa6:/lh.white.gii",
+            "colormap": "label_fsa6_lh:",
+            "hemisphere": "left"
+          },
+          {
+            "mesh": "rootUrl:fsa_fsa6:/rh.white.gii",
+            "colormap": "label_fsa6_rh:",
+            "hemisphere": "right"
+          }
+        ]
+      },
+      {
+        "name": "fsaverage/fsaverage6/inflated",
+        "meshes": [
+          {
+            "mesh": "rootUrl2:/fsa6-translated-inflated/lh.inflated.surf.gii",
+            "colormap": "label_fsa6_lh:",
+            "hemisphere": "left"
+          },
+          {
+            "mesh": "rootUrl2:/fsa6-translated-inflated/rh.inflated.surf.gii",
+            "colormap": "label_fsa6_rh:",
+            "hemisphere": "right"
+          }
+        ]
+      },
+      {
+        "name": "fsaverage/fsaverage6/sphere",
+        "meshes": [
+          {
+            "mesh": "rootUrl:fsa_fsa6:/lh.sphere.gii",
+            "colormap": "label_fsa6_lh:",
+            "hemisphere": "left"
+          },
+          {
+            "mesh": "rootUrl:fsa_fsa6:/rh.sphere.gii",
+            "colormap": "label_fsa6_rh:",
+            "hemisphere": "right"
+          }
+        ]
+      }
+    ]
+  },
+  "parcellations": [
+    {
+      "@id": "julich/tmp/parcellation/freesurfer-test-parc",
+      "@type": "julich/tmp/parcellation",
+      "name": "Freesurfer Test Parc",
+      "properties": {
+        "name": "Freesurfer Test Parc",
+        "descrption": "This is a test parc for free surfer"
+      },
+      "regions": [
+        {
+          "name": "right",
+          "children": [
+            {
+              "name": "Area TE 1.1 (HESCHL)",
+              "num": "0",
+              "id": "33",
+              "grayvalue": "1",
+              "iav": {
+                "name": "Area TE 1.1 (HESCHL)",
+                "labelIndex": 39,
+                "rgb": [
+                  8,
+                  113,
+                  68
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area TE 1.1 (HESCHL)",
+                    "gray": "1"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 3b (PostCG)",
+              "num": "1",
+              "id": "127",
+              "grayvalue": "2",
+              "iav": {
+                "name": "Area 3b (PostCG)",
+                "labelIndex": 37,
+                "rgb": [
+                  239,
+                  246,
+                  155
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 3b (PostCG)",
+                    "gray": "2"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area PF (IPL)",
+              "num": "2",
+              "id": "206",
+              "grayvalue": "3",
+              "iav": {
+                "name": "Area PF (IPL)",
+                "labelIndex": 12,
+                "rgb": [
+                  226,
+                  211,
+                  61
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area PF (IPL)",
+                    "gray": "5"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hOc4la (LOC)",
+              "num": "3",
+              "id": "118",
+              "grayvalue": "4",
+              "iav": {
+                "name": "Area hOc4la (LOC)",
+                "labelIndex": 29,
+                "rgb": [
+                  233,
+                  168,
+                  189
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hOc4la (LOC)",
+                    "gray": "4"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hOc2 (V2, 18)",
+              "num": "4",
+              "id": "7",
+              "grayvalue": "5",
+              "iav": {
+                "name": "Area hOc2 (V2, 18)",
+                "labelIndex": 21,
+                "rgb": [
+                  84,
+                  110,
+                  22
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hOc2 (V2, 18)",
+                    "gray": "6"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 6d2 (PreCG)",
+              "num": "5",
+              "id": "288",
+              "grayvalue": "6",
+              "iav": {
+                "name": "Area 6d2 (PreCG)",
+                "labelIndex": 82,
+                "rgb": [
+                  170,
+                  151,
+                  180
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 6d2 (PreCG)",
+                    "gray": "7"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Ig3 (Insula)",
+              "num": "6",
+              "id": "336",
+              "grayvalue": "7",
+              "iav": {
+                "name": "Area Ig3 (Insula)",
+                "labelIndex": 105,
+                "rgb": [
+                  105,
+                  253,
+                  197
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Ig3 (Insula)",
+                    "gray": "8"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hOc4d (Cuneus)",
+              "num": "7",
+              "id": "119",
+              "grayvalue": "8",
+              "iav": {
+                "name": "Area hOc4d (Cuneus)",
+                "labelIndex": 44,
+                "rgb": [
+                  109,
+                  218,
+                  10
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hOc4d (Cuneus)",
+                    "gray": "9"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Fo4 (OFC)",
+              "num": "8",
+              "id": "324",
+              "grayvalue": "9",
+              "iav": {
+                "name": "Area Fo4 (OFC)",
+                "labelIndex": 80,
+                "rgb": [
+                  163,
+                  204,
+                  53
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Fo4 (OFC)",
+                    "gray": "10"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area OP4 (POperc)",
+              "num": "9",
+              "id": "72",
+              "grayvalue": "10",
+              "iav": {
+                "name": "Area OP4 (POperc)",
+                "labelIndex": 14,
+                "rgb": [
+                  89,
+                  80,
+                  132
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area OP4 (POperc)",
+                    "gray": "11"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 1 (PostCG)",
+              "num": "10",
+              "id": "125",
+              "grayvalue": "11",
+              "iav": {
+                "name": "Area 1 (PostCG)",
+                "labelIndex": 116,
+                "rgb": [
+                  232,
+                  185,
+                  250
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 1 (PostCG)",
+                    "gray": "12"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Ch 4 (Basal Forebrain)",
+              "num": "11",
+              "id": "264",
+              "grayvalue": "12",
+              "iav": {
+                "name": "Ch 4 (Basal Forebrain)",
+                "labelIndex": 97,
+                "rgb": [
+                  200,
+                  200,
+                  200
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Ch 4 (Basal Forebrain)",
+                    "gray": "13"
+                  }
+                }
+              }
+            },
+            {
+              "name": "GapMap Frontal-I (GapMap)",
+              "num": "12",
+              "id": "500",
+              "grayvalue": "13",
+              "iav": {
+                "name": "Frontal-I (GapMap)",
+                "labelIndex": 71,
+                "rgb": [
+                  186,
+                  255,
+                  25
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Frontal-I (GapMap)",
+                    "gray": "14"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hPO1 (POS)",
+              "num": "13",
+              "id": "297",
+              "grayvalue": "14",
+              "iav": {
+                "name": "Area hPO1 (POS)",
+                "labelIndex": 69,
+                "rgb": [
+                  153,
+                  232,
+                  235
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hPO1 (POS)",
+                    "gray": "15"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area OP2 (POperc)",
+              "num": "14",
+              "id": "74",
+              "grayvalue": "15",
+              "iav": {
+                "name": "Area OP2 (POperc)",
+                "labelIndex": 122,
+                "rgb": [
+                  36,
+                  47,
+                  221
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area OP2 (POperc)",
+                    "gray": "17"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area TE 1.0 (HESCHL)",
+              "num": "15",
+              "id": "27",
+              "grayvalue": "16",
+              "iav": {
+                "name": "Area TE 1.0 (HESCHL)",
+                "labelIndex": 34,
+                "rgb": [
+                  252,
+                  84,
+                  222
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area TE 1.0 (HESCHL)",
+                    "gray": "18"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Dorsal Dentate Nucleus (Cerebellum)",
+              "num": "16",
+              "id": "240",
+              "grayvalue": "17",
+              "iav": {
+                "name": "Dorsal Dentate Nucleus (Cerebellum)",
+                "labelIndex": 111,
+                "rgb": [
+                  200,
+                  200,
+                  200
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Dorsal Dentate Nucleus (Cerebellum)",
+                    "gray": "16"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area STS2 (STS)",
+              "num": "17",
+              "id": "272",
+              "grayvalue": "18",
+              "iav": {
+                "name": "Area STS2 (STS)",
+                "labelIndex": 98,
+                "rgb": [
+                  62,
+                  117,
+                  123
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area STS2 (STS)",
+                    "gray": "19"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Id3 (Insula)",
+              "num": "18",
+              "id": "57",
+              "grayvalue": "19",
+              "iav": {
+                "name": "Area Id3 (Insula)",
+                "labelIndex": 54,
+                "rgb": [
+                  32,
+                  32,
+                  58
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Id3 (Insula)",
+                    "gray": "20"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 6mp (SMA, mesial SFG)",
+              "num": "19",
+              "id": "298",
+              "grayvalue": "20",
+              "iav": {
+                "name": "Area 6mp (SMA, mesial SFG)",
+                "labelIndex": 108,
+                "rgb": [
+                  75,
+                  95,
+                  87
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 6mp (SMA, mesial SFG)",
+                    "gray": "21"
+                  }
+                }
+              }
+            },
+            {
+              "name": "DG (Hippocampus)",
+              "num": "20",
+              "id": "61",
+              "grayvalue": "21",
+              "iav": {
+                "name": "DG (Hippocampus)",
+                "labelIndex": 91,
+                "rgb": [
+                  0,
+                  149,
+                  8
+                ],
+                "_": {
+                  "xml": {
+                    "name": "DG (Hippocampus)",
+                    "gray": "22"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area PGp (IPL)",
+              "num": "21",
+              "id": "108",
+              "grayvalue": "22",
+              "iav": {
+                "name": "Area PGp (IPL)",
+                "labelIndex": 65,
+                "rgb": [
+                  92,
+                  116,
+                  83
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area PGp (IPL)",
+                    "gray": "23"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Ig1 (Insula)",
+              "num": "22",
+              "id": "115",
+              "grayvalue": "23",
+              "iav": {
+                "name": "Area Ig1 (Insula)",
+                "labelIndex": 25,
+                "rgb": [
+                  18,
+                  111,
+                  40
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Ig1 (Insula)",
+                    "gray": "24"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 6ma (preSMA, mesial SFG)",
+              "num": "23",
+              "id": "299",
+              "grayvalue": "24",
+              "iav": {
+                "name": "Area 6ma (preSMA, mesial SFG)",
+                "labelIndex": 70,
+                "rgb": [
+                  204,
+                  108,
+                  222
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 6ma (preSMA, mesial SFG)",
+                    "gray": "25"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area TI (STG)",
+              "num": "24",
+              "id": "243",
+              "grayvalue": "25",
+              "iav": {
+                "name": "Area TI (STG)",
+                "labelIndex": 13,
+                "rgb": [
+                  86,
+                  101,
+                  22
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area TI (STG)",
+                    "gray": "26"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area p24ab (pACC)",
+              "num": "25",
+              "id": "231",
+              "grayvalue": "26",
+              "iav": {
+                "name": "Area p24ab (pACC)",
+                "labelIndex": 63,
+                "rgb": [
+                  153,
+                  195,
+                  229
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area p24ab (pACC)",
+                    "gray": "28"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area PFt (IPL)",
+              "num": "26",
+              "id": "109",
+              "grayvalue": "27",
+              "iav": {
+                "name": "Area PFt (IPL)",
+                "labelIndex": 20,
+                "rgb": [
+                  120,
+                  135,
+                  232
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area PFt (IPL)",
+                    "gray": "27"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area p24c (pACC)",
+              "num": "27",
+              "id": "232",
+              "grayvalue": "28",
+              "iav": {
+                "name": "Area p24c (pACC)",
+                "labelIndex": 57,
+                "rgb": [
+                  241,
+                  164,
+                  195
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area p24c (pACC)",
+                    "gray": "29"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hOc3v (LingG)",
+              "num": "28",
+              "id": "10",
+              "grayvalue": "29",
+              "iav": {
+                "name": "Area hOc3v (LingG)",
+                "labelIndex": 77,
+                "rgb": [
+                  83,
+                  179,
+                  155
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hOc3v (LingG)",
+                    "gray": "30"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area TE 3 (STG)",
+              "num": "29",
+              "id": "31",
+              "grayvalue": "30",
+              "iav": {
+                "name": "Area TE 3 (STG)",
+                "labelIndex": 15,
+                "rgb": [
+                  159,
+                  104,
+                  108
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area TE 3 (STG)",
+                    "gray": "31"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area PFcm (IPL)",
+              "num": "30",
+              "id": "113",
+              "grayvalue": "31",
+              "iav": {
+                "name": "Area PFcm (IPL)",
+                "labelIndex": 103,
+                "rgb": [
+                  98,
+                  128,
+                  120
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area PFcm (IPL)",
+                    "gray": "32"
+                  }
+                }
+              }
+            },
+            {
+              "name": "GapMap Frontal-to-Occipital (GapMap)",
+              "num": "31",
+              "id": "504",
+              "grayvalue": "32",
+              "iav": {
+                "name": "Frontal-to-Occipital (GapMap)",
+                "labelIndex": 33,
+                "rgb": [
+                  104,
+                  161,
+                  93
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Frontal-to-Occipital (GapMap)",
+                    "gray": "33"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hIP5 (IPS)",
+              "num": "32",
+              "id": "295",
+              "grayvalue": "33",
+              "iav": {
+                "name": "Area hIP5 (IPS)",
+                "labelIndex": 7,
+                "rgb": [
+                  217,
+                  87,
+                  210
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hIP5 (IPS)",
+                    "gray": "34"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 7P (SPL)",
+              "num": "33",
+              "id": "208",
+              "grayvalue": "34",
+              "iav": {
+                "name": "Area 7P (SPL)",
+                "labelIndex": 24,
+                "rgb": [
+                  52,
+                  20,
+                  106
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 7P (SPL)",
+                    "gray": "35"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 6d1 (PreCG)",
+              "num": "34",
+              "id": "287",
+              "grayvalue": "35",
+              "iav": {
+                "name": "Area 6d1 (PreCG)",
+                "labelIndex": 90,
+                "rgb": [
+                  45,
+                  33,
+                  27
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 6d1 (PreCG)",
+                    "gray": "36"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area FG1 (FusG)",
+              "num": "35",
+              "id": "107",
+              "grayvalue": "36",
+              "iav": {
+                "name": "Area FG1 (FusG)",
+                "labelIndex": 84,
+                "rgb": [
+                  131,
+                  183,
+                  58
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area FG1 (FusG)",
+                    "gray": "37"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hIP4 (IPS)",
+              "num": "36",
+              "id": "294",
+              "grayvalue": "37",
+              "iav": {
+                "name": "Area hIP4 (IPS)",
+                "labelIndex": 115,
+                "rgb": [
+                  254,
+                  52,
+                  184
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hIP4 (IPS)",
+                    "gray": "38"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area OP6 (Frontal Operculum)",
+              "num": "37",
+              "id": "349",
+              "grayvalue": "38",
+              "iav": {
+                "name": "Area OP6 (Frontal Operculum)",
+                "labelIndex": 31,
+                "rgb": [
+                  138,
+                  63,
+                  100
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area OP6 (Frontal Operculum)",
+                    "gray": "39"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area FG2 (FusG)",
+              "num": "38",
+              "id": "106",
+              "grayvalue": "39",
+              "iav": {
+                "name": "Area FG2 (FusG)",
+                "labelIndex": 28,
+                "rgb": [
+                  67,
+                  94,
+                  149
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area FG2 (FusG)",
+                    "gray": "40"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hIP6 (IPS)",
+              "num": "39",
+              "id": "292",
+              "grayvalue": "40",
+              "iav": {
+                "name": "Area hIP6 (IPS)",
+                "labelIndex": 95,
+                "rgb": [
+                  237,
+                  233,
+                  37
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hIP6 (IPS)",
+                    "gray": "41"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hIP3 (IPS)",
+              "num": "40",
+              "id": "133",
+              "grayvalue": "41",
+              "iav": {
+                "name": "Area hIP3 (IPS)",
+                "labelIndex": 75,
+                "rgb": [
+                  113,
+                  172,
+                  229
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hIP3 (IPS)",
+                    "gray": "43"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Fo5 (OFC)",
+              "num": "41",
+              "id": "325",
+              "grayvalue": "42",
+              "iav": {
+                "name": "Area Fo5 (OFC)",
+                "labelIndex": 76,
+                "rgb": [
+                  219,
+                  11,
+                  91
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Fo5 (OFC)",
+                    "gray": "42"
+                  }
+                }
+              }
+            },
+            {
+              "name": "CM (Amygdala)",
+              "num": "42",
+              "id": "290",
+              "grayvalue": "43",
+              "iav": {
+                "name": "CM (Amygdala)",
+                "labelIndex": 85,
+                "rgb": [
+                  200,
+                  200,
+                  200
+                ],
+                "_": {
+                  "xml": {
+                    "name": "CM (Amygdala)",
+                    "gray": "44"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area OP7 (Frontal Operculum)",
+              "num": "43",
+              "id": "350",
+              "grayvalue": "44",
+              "iav": {
+                "name": "Area OP7 (Frontal Operculum)",
+                "labelIndex": 60,
+                "rgb": [
+                  134,
+                  116,
+                  192
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area OP7 (Frontal Operculum)",
+                    "gray": "45"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 4p (PreCG)",
+              "num": "44",
+              "id": "123",
+              "grayvalue": "45",
+              "iav": {
+                "name": "Area 4p (PreCG)",
+                "labelIndex": 9,
+                "rgb": [
+                  116,
+                  92,
+                  124
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 4p (PreCG)",
+                    "gray": "46"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 33 (ACC)",
+              "num": "45",
+              "id": "39",
+              "grayvalue": "46",
+              "iav": {
+                "name": "Area 33 (ACC)",
+                "labelIndex": 96,
+                "rgb": [
+                  51,
+                  57,
+                  245
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 33 (ACC)",
+                    "gray": "47"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hOc6 (POS)",
+              "num": "46",
+              "id": "291",
+              "grayvalue": "47",
+              "iav": {
+                "name": "Area hOc6 (POS)",
+                "labelIndex": 72,
+                "rgb": [
+                  239,
+                  66,
+                  26
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hOc6 (POS)",
+                    "gray": "49"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Ig2 (Insula)",
+              "num": "47",
+              "id": "114",
+              "grayvalue": "48",
+              "iav": {
+                "name": "Area Ig2 (Insula)",
+                "labelIndex": 45,
+                "rgb": [
+                  105,
+                  61,
+                  82
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Ig2 (Insula)",
+                    "gray": "50"
+                  }
+                }
+              }
+            },
+            {
+              "name": "CA2 (Hippocampus)",
+              "num": "48",
+              "id": "58",
+              "grayvalue": "49",
+              "iav": {
+                "name": "CA2 (Hippocampus)",
+                "labelIndex": 23,
+                "rgb": [
+                  0,
+                  250,
+                  98
+                ],
+                "_": {
+                  "xml": {
+                    "name": "CA2 (Hippocampus)",
+                    "gray": "51"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Fp2 (FPole)",
+              "num": "49",
+              "id": "211",
+              "grayvalue": "50",
+              "iav": {
+                "name": "Area Fp2 (FPole)",
+                "labelIndex": 101,
+                "rgb": [
+                  239,
+                  137,
+                  211
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Fp2 (FPole)",
+                    "gray": "52"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Entorhinal Cortex",
+              "num": "50",
+              "id": "60",
+              "grayvalue": "51",
+              "iav": {
+                "name": "Entorhinal Cortex",
+                "labelIndex": 64,
+                "rgb": [
+                  234,
+                  239,
+                  14
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Entorhinal Cortex",
+                    "gray": "53"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Id4 (Insula)",
+              "num": "51",
+              "id": "337",
+              "grayvalue": "52",
+              "iav": {
+                "name": "Area Id4 (Insula)",
+                "labelIndex": 53,
+                "rgb": [
+                  38,
+                  174,
+                  113
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Id4 (Insula)",
+                    "gray": "54"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Id2 (Insula)",
+              "num": "52",
+              "id": "56",
+              "grayvalue": "53",
+              "iav": {
+                "name": "Area Id2 (Insula)",
+                "labelIndex": 99,
+                "rgb": [
+                  225,
+                  126,
+                  73
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Id2 (Insula)",
+                    "gray": "55"
+                  }
+                }
+              }
+            },
+            {
+              "name": "CA1 (Hippocampus)",
+              "num": "53",
+              "id": "66",
+              "grayvalue": "54",
+              "iav": {
+                "name": "CA1 (Hippocampus)",
+                "labelIndex": 61,
+                "rgb": [
+                  250,
+                  89,
+                  0
+                ],
+                "_": {
+                  "xml": {
+                    "name": "CA1 (Hippocampus)",
+                    "gray": "57"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area OP1 (POperc)",
+              "num": "54",
+              "id": "73",
+              "grayvalue": "55",
+              "iav": {
+                "name": "Area OP1 (POperc)",
+                "labelIndex": 1,
+                "rgb": [
+                  250,
+                  182,
+                  34
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area OP1 (POperc)",
+                    "gray": "56"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Id6 (Insula)",
+              "num": "55",
+              "id": "340",
+              "grayvalue": "56",
+              "iav": {
+                "name": "Area Id6 (Insula)",
+                "labelIndex": 11,
+                "rgb": [
+                  138,
+                  127,
+                  119
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Id6 (Insula)",
+                    "gray": "58"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hIP8 (IPS)",
+              "num": "56",
+              "id": "293",
+              "grayvalue": "57",
+              "iav": {
+                "name": "Area hIP8 (IPS)",
+                "labelIndex": 50,
+                "rgb": [
+                  223,
+                  109,
+                  3
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hIP8 (IPS)",
+                    "gray": "60"
+                  }
+                }
+              }
+            },
+            {
+              "name": "IF (Amygdala)",
+              "num": "57",
+              "id": "237",
+              "grayvalue": "58",
+              "iav": {
+                "name": "IF (Amygdala)",
+                "labelIndex": 46,
+                "rgb": [
+                  200,
+                  200,
+                  200
+                ],
+                "_": {
+                  "xml": {
+                    "name": "IF (Amygdala)",
+                    "gray": "61"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area OP9 (Frontal Operculum)",
+              "num": "58",
+              "id": "274",
+              "grayvalue": "59",
+              "iav": {
+                "name": "Area OP9 (Frontal Operculum)",
+                "labelIndex": 117,
+                "rgb": [
+                  175,
+                  123,
+                  34
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area OP9 (Frontal Operculum)",
+                    "gray": "62"
+                  }
+                }
+              }
+            },
+            {
+              "name": "GapMap Temporal-to-Parietal (GapMap)",
+              "num": "59",
+              "id": "503",
+              "grayvalue": "60",
+              "iav": {
+                "name": "Temporal-to-Parietal (GapMap)",
+                "labelIndex": 2,
+                "rgb": [
+                  76,
+                  90,
+                  161
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Temporal-to-Parietal (GapMap)",
+                    "gray": "64"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Ventral Dentate Nucleus (Cerebellum)",
+              "num": "60",
+              "id": "241",
+              "grayvalue": "61",
+              "iav": {
+                "name": "Ventral Dentate Nucleus (Cerebellum)",
+                "labelIndex": 38,
+                "rgb": [
+                  200,
+                  200,
+                  200
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Ventral Dentate Nucleus (Cerebellum)",
+                    "gray": "65"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 6d3 (SFS)",
+              "num": "61",
+              "id": "289",
+              "grayvalue": "62",
+              "iav": {
+                "name": "Area 6d3 (SFS)",
+                "labelIndex": 68,
+                "rgb": [
+                  55,
+                  239,
+                  21
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 6d3 (SFS)",
+                    "gray": "66"
+                  }
+                }
+              }
+            },
+            {
+              "name": "HATA (Hippocampus)",
+              "num": "62",
+              "id": "68",
+              "grayvalue": "63",
+              "iav": {
+                "name": "HATA (Hippocampus)",
+                "labelIndex": 56,
+                "rgb": [
+                  137,
+                  12,
+                  73
+                ],
+                "_": {
+                  "xml": {
+                    "name": "HATA (Hippocampus)",
+                    "gray": "67"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hIP2 (IPS)",
+              "num": "63",
+              "id": "129",
+              "grayvalue": "64",
+              "iav": {
+                "name": "Area hIP2 (IPS)",
+                "labelIndex": 59,
+                "rgb": [
+                  127,
+                  245,
+                  203
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hIP2 (IPS)",
+                    "gray": "68"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 44 (IFG)",
+              "num": "64",
+              "id": "2",
+              "grayvalue": "65",
+              "iav": {
+                "name": "Area 44 (IFG)",
+                "labelIndex": 92,
+                "rgb": [
+                  54,
+                  74,
+                  75
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 44 (IFG)",
+                    "gray": "69"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Fo7 (OFC)",
+              "num": "65",
+              "id": "327",
+              "grayvalue": "66",
+              "iav": {
+                "name": "Area Fo7 (OFC)",
+                "labelIndex": 52,
+                "rgb": [
+                  64,
+                  211,
+                  186
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Fo7 (OFC)",
+                    "gray": "70"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area a29 (retrosplenial)",
+              "num": "66",
+              "id": "353",
+              "grayvalue": "67"
+            },
+            {
+              "name": "Area 45 (IFG)",
+              "num": "67",
+              "id": "1",
+              "grayvalue": "68",
+              "iav": {
+                "name": "Area 45 (IFG)",
+                "labelIndex": 112,
+                "rgb": [
+                  167,
+                  103,
+                  146
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 45 (IFG)",
+                    "gray": "71"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Id1 (Insula)",
+              "num": "68",
+              "id": "116",
+              "grayvalue": "69",
+              "iav": {
+                "name": "Area Id1 (Insula)",
+                "labelIndex": 78,
+                "rgb": [
+                  141,
+                  112,
+                  216
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Id1 (Insula)",
+                    "gray": "72"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 5Ci (SPL)",
+              "num": "69",
+              "id": "136",
+              "grayvalue": "70",
+              "iav": {
+                "name": "Area 5Ci (SPL)",
+                "labelIndex": 88,
+                "rgb": [
+                  79,
+                  242,
+                  146
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 5Ci (SPL)",
+                    "gray": "73"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area TE 1.2 (HESCHL)",
+              "num": "70",
+              "id": "30",
+              "grayvalue": "71",
+              "iav": {
+                "name": "Area TE 1.2 (HESCHL)",
+                "labelIndex": 86,
+                "rgb": [
+                  202,
+                  251,
+                  192
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area TE 1.2 (HESCHL)",
+                    "gray": "74"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Id5 (Insula)",
+              "num": "71",
+              "id": "338",
+              "grayvalue": "72",
+              "iav": {
+                "name": "Area Id5 (Insula)",
+                "labelIndex": 104,
+                "rgb": [
+                  112,
+                  6,
+                  50
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Id5 (Insula)",
+                    "gray": "75"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area PGa (IPL)",
+              "num": "72",
+              "id": "110",
+              "grayvalue": "73",
+              "iav": {
+                "name": "Area PGa (IPL)",
+                "labelIndex": 81,
+                "rgb": [
+                  42,
+                  236,
+                  131
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area PGa (IPL)",
+                    "gray": "76"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hIP1 (IPS)",
+              "num": "73",
+              "id": "128",
+              "grayvalue": "74",
+              "iav": {
+                "name": "Area hIP1 (IPS)",
+                "labelIndex": 32,
+                "rgb": [
+                  66,
+                  149,
+                  82
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hIP1 (IPS)",
+                    "gray": "77"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 2 (PostCS)",
+              "num": "74",
+              "id": "252",
+              "grayvalue": "75",
+              "iav": {
+                "name": "Area 2 (PostCS)",
+                "labelIndex": 49,
+                "rgb": [
+                  23,
+                  13,
+                  35
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 2 (PostCS)",
+                    "gray": "78"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Ia (Insula)",
+              "num": "75",
+              "id": "339",
+              "grayvalue": "76",
+              "iav": {
+                "name": "Area Ia (Insula)",
+                "labelIndex": 118,
+                "rgb": [
+                  71,
+                  217,
+                  62
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Ia (Insula)",
+                    "gray": "80"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Ch 123 (Basal Forebrain)",
+              "num": "76",
+              "id": "286",
+              "grayvalue": "77",
+              "iav": {
+                "name": "Ch 123 (Basal Forebrain)",
+                "labelIndex": 62,
+                "rgb": [
+                  200,
+                  200,
+                  200
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Ch 123 (Basal Forebrain)",
+                    "gray": "79"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area TE 2.1 (STG)",
+              "num": "77",
+              "id": "28",
+              "grayvalue": "78",
+              "iav": {
+                "name": "Area TE 2.1 (STG)",
+                "labelIndex": 19,
+                "rgb": [
+                  67,
+                  254,
+                  44
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area TE 2.1 (STG)",
+                    "gray": "82"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hOc3d (Cuneus)",
+              "num": "78",
+              "id": "120",
+              "grayvalue": "79",
+              "iav": {
+                "name": "Area hOc3d (Cuneus)",
+                "labelIndex": 106,
+                "rgb": [
+                  105,
+                  191,
+                  48
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hOc3d (Cuneus)",
+                    "gray": "81"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area OP3 (POperc)",
+              "num": "79",
+              "id": "75",
+              "grayvalue": "80",
+              "iav": {
+                "name": "Area OP3 (POperc)",
+                "labelIndex": 22,
+                "rgb": [
+                  58,
+                  122,
+                  80
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area OP3 (POperc)",
+                    "gray": "84"
+                  }
+                }
+              }
+            },
+            {
+              "name": "VTM (Amygdala)",
+              "num": "80",
+              "id": "228",
+              "grayvalue": "81",
+              "iav": {
+                "name": "VTM (Amygdala)",
+                "labelIndex": 109,
+                "rgb": [
+                  200,
+                  200,
+                  200
+                ],
+                "_": {
+                  "xml": {
+                    "name": "VTM (Amygdala)",
+                    "gray": "83"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 7A (SPL)",
+              "num": "81",
+              "id": "134",
+              "grayvalue": "82",
+              "iav": {
+                "name": "Area 7A (SPL)",
+                "labelIndex": 17,
+                "rgb": [
+                  38,
+                  204,
+                  19
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 7A (SPL)",
+                    "gray": "85"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Subiculum (Hippocampus)",
+              "num": "82",
+              "id": "192",
+              "grayvalue": "83"
+            },
+            {
+              "name": "Area OP8 (Frontal Operculum)",
+              "num": "83",
+              "id": "273",
+              "grayvalue": "84",
+              "iav": {
+                "name": "Area OP8 (Frontal Operculum)",
+                "labelIndex": 40,
+                "rgb": [
+                  29,
+                  76,
+                  168
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area OP8 (Frontal Operculum)",
+                    "gray": "86"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area p29 (retrosplenial)",
+              "num": "84",
+              "id": "268",
+              "grayvalue": "85"
+            },
+            {
+              "name": "Area 25 (sACC)",
+              "num": "85",
+              "id": "184",
+              "grayvalue": "86",
+              "iav": {
+                "name": "Area 25 (sACC)",
+                "labelIndex": 3,
+                "rgb": [
+                  170,
+                  68,
+                  220
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 25 (sACC)",
+                    "gray": "88"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area OP5 (Frontal Operculum)",
+              "num": "86",
+              "id": "348",
+              "grayvalue": "87",
+              "iav": {
+                "name": "Area OP5 (Frontal Operculum)",
+                "labelIndex": 43,
+                "rgb": [
+                  222,
+                  78,
+                  253
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area OP5 (Frontal Operculum)",
+                    "gray": "90"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area PFm (IPL)",
+              "num": "87",
+              "id": "112",
+              "grayvalue": "88",
+              "iav": {
+                "name": "Area PFm (IPL)",
+                "labelIndex": 102,
+                "rgb": [
+                  53,
+                  76,
+                  145
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area PFm (IPL)",
+                    "gray": "92"
+                  }
+                }
+              }
+            },
+            {
+              "name": "CA3 (Hippocampus)",
+              "num": "88",
+              "id": "59",
+              "grayvalue": "89",
+              "iav": {
+                "name": "CA3 (Hippocampus)",
+                "labelIndex": 119,
+                "rgb": [
+                  39,
+                  0,
+                  250
+                ],
+                "_": {
+                  "xml": {
+                    "name": "CA3 (Hippocampus)",
+                    "gray": "91"
+                  }
+                }
+              }
+            },
+            {
+              "name": "GapMap Frontal-II (GapMap)",
+              "num": "89",
+              "id": "501",
+              "grayvalue": "90",
+              "iav": {
+                "name": "Frontal-II (GapMap)",
+                "labelIndex": 27,
+                "rgb": [
+                  255,
+                  135,
+                  15
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Frontal-II (GapMap)",
+                    "gray": "93"
+                  }
+                }
+              }
+            },
+            {
+              "name": "LB (Amygdala)",
+              "num": "90",
+              "id": "187",
+              "grayvalue": "91",
+              "iav": {
+                "name": "LB (Amygdala)",
+                "labelIndex": 41,
+                "rgb": [
+                  200,
+                  200,
+                  200
+                ],
+                "_": {
+                  "xml": {
+                    "name": "LB (Amygdala)",
+                    "gray": "94"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area STS1 (STS)",
+              "num": "91",
+              "id": "271",
+              "grayvalue": "92",
+              "iav": {
+                "name": "Area STS1 (STS)",
+                "labelIndex": 114,
+                "rgb": [
+                  205,
+                  228,
+                  4
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area STS1 (STS)",
+                    "gray": "96"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 4a (PreCG)",
+              "num": "92",
+              "id": "124",
+              "grayvalue": "93",
+              "iav": {
+                "name": "Area 4a (PreCG)",
+                "labelIndex": 100,
+                "rgb": [
+                  118,
+                  239,
+                  183
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 4a (PreCG)",
+                    "gray": "95"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 5L (SPL)",
+              "num": "93",
+              "id": "130",
+              "grayvalue": "94",
+              "iav": {
+                "name": "Area 5L (SPL)",
+                "labelIndex": 83,
+                "rgb": [
+                  184,
+                  185,
+                  58
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 5L (SPL)",
+                    "gray": "98"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 5M (SPL)",
+              "num": "94",
+              "id": "131",
+              "grayvalue": "95",
+              "iav": {
+                "name": "Area 5M (SPL)",
+                "labelIndex": 26,
+                "rgb": [
+                  225,
+                  245,
+                  76
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 5M (SPL)",
+                    "gray": "97"
+                  }
+                }
+              }
+            },
+            {
+              "name": "GapMap Frontal-to-Temporal (GapMap)",
+              "num": "95",
+              "id": "502",
+              "grayvalue": "96",
+              "iav": {
+                "name": "Frontal-to-Temporal (GapMap)",
+                "labelIndex": 110,
+                "rgb": [
+                  161,
+                  24,
+                  74
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Frontal-to-Temporal (GapMap)",
+                    "gray": "100"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area FG3 (FusG)",
+              "num": "96",
+              "id": "239",
+              "grayvalue": "97",
+              "iav": {
+                "name": "Area FG3 (FusG)",
+                "labelIndex": 42,
+                "rgb": [
+                  120,
+                  147,
+                  37
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area FG3 (FusG)",
+                    "gray": "99"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area i30 (retrosplenial)",
+              "num": "97",
+              "id": "267",
+              "grayvalue": "98"
+            },
+            {
+              "name": "Area p30 (retrosplenial)",
+              "num": "98",
+              "id": "269",
+              "grayvalue": "99"
+            },
+            {
+              "name": "Area Fp1 (FPole)",
+              "num": "99",
+              "id": "212",
+              "grayvalue": "100",
+              "iav": {
+                "name": "Area Fp1 (FPole)",
+                "labelIndex": 107,
+                "rgb": [
+                  226,
+                  14,
+                  200
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Fp1 (FPole)",
+                    "gray": "101"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area a30 (retrosplenial)",
+              "num": "100",
+              "id": "354",
+              "grayvalue": "101"
+            },
+            {
+              "name": "Fastigial Nucleus (Cerebellum)",
+              "num": "101",
+              "id": "219",
+              "grayvalue": "102",
+              "iav": {
+                "name": "Fastigial Nucleus (Cerebellum)",
+                "labelIndex": 89,
+                "rgb": [
+                  200,
+                  200,
+                  200
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Fastigial Nucleus (Cerebellum)",
+                    "gray": "103"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area s32 (sACC)",
+              "num": "102",
+              "id": "46",
+              "grayvalue": "103",
+              "iav": {
+                "name": "Area s32 (sACC)",
+                "labelIndex": 74,
+                "rgb": [
+                  193,
+                  94,
+                  250
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area s32 (sACC)",
+                    "gray": "102"
+                  }
+                }
+              }
+            },
+            {
+              "name": "MF (Amygdala)",
+              "num": "103",
+              "id": "235",
+              "grayvalue": "104",
+              "iav": {
+                "name": "MF (Amygdala)",
+                "labelIndex": 123,
+                "rgb": [
+                  200,
+                  200,
+                  200
+                ],
+                "_": {
+                  "xml": {
+                    "name": "MF (Amygdala)",
+                    "gray": "104"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 3a (PostCG)",
+              "num": "104",
+              "id": "126",
+              "grayvalue": "105",
+              "iav": {
+                "name": "Area 3a (PostCG)",
+                "labelIndex": 121,
+                "rgb": [
+                  187,
+                  133,
+                  50
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 3a (PostCG)",
+                    "gray": "105"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Id7 (Insula)",
+              "num": "105",
+              "id": "159",
+              "grayvalue": "106",
+              "iav": {
+                "name": "Area Id7 (Insula)",
+                "labelIndex": 18,
+                "rgb": [
+                  188,
+                  250,
+                  216
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Id7 (Insula)",
+                    "gray": "107"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hOc5 (LOC)",
+              "num": "106",
+              "id": "6",
+              "grayvalue": "107",
+              "iav": {
+                "name": "Area hOc5 (LOC)",
+                "labelIndex": 16,
+                "rgb": [
+                  255,
+                  0,
+                  0
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hOc5 (LOC)",
+                    "gray": "106"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area FG4 (FusG)",
+              "num": "107",
+              "id": "238",
+              "grayvalue": "108",
+              "iav": {
+                "name": "Area FG4 (FusG)",
+                "labelIndex": 55,
+                "rgb": [
+                  170,
+                  220,
+                  175
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area FG4 (FusG)",
+                    "gray": "108"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Interposed Nucleus (Cerebellum)",
+              "num": "108",
+              "id": "251",
+              "grayvalue": "109",
+              "iav": {
+                "name": "Interposed Nucleus (Cerebellum)",
+                "labelIndex": 30,
+                "rgb": [
+                  200,
+                  200,
+                  200
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Interposed Nucleus (Cerebellum)",
+                    "gray": "109"
+                  }
+                }
+              }
+            },
+            {
+              "name": "SF (Amygdala)",
+              "num": "109",
+              "id": "185",
+              "grayvalue": "110",
+              "iav": {
+                "name": "SF (Amygdala)",
+                "labelIndex": 5,
+                "rgb": [
+                  200,
+                  200,
+                  200
+                ],
+                "_": {
+                  "xml": {
+                    "name": "SF (Amygdala)",
+                    "gray": "110"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Fo1 (OFC)",
+              "num": "110",
+              "id": "3",
+              "grayvalue": "111",
+              "iav": {
+                "name": "Area Fo1 (OFC)",
+                "labelIndex": 47,
+                "rgb": [
+                  7,
+                  255,
+                  179
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Fo1 (OFC)",
+                    "gray": "111"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area s24 (sACC)",
+              "num": "111",
+              "id": "183",
+              "grayvalue": "112",
+              "iav": {
+                "name": "Area s24 (sACC)",
+                "labelIndex": 73,
+                "rgb": [
+                  133,
+                  34,
+                  201
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area s24 (sACC)",
+                    "gray": "112"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Fo6 (OFC)",
+              "num": "112",
+              "id": "326",
+              "grayvalue": "113",
+              "iav": {
+                "name": "Area Fo6 (OFC)",
+                "labelIndex": 36,
+                "rgb": [
+                  199,
+                  156,
+                  187
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Fo6 (OFC)",
+                    "gray": "113"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area PFop (IPL)",
+              "num": "113",
+              "id": "111",
+              "grayvalue": "114",
+              "iav": {
+                "name": "Area PFop (IPL)",
+                "labelIndex": 94,
+                "rgb": [
+                  146,
+                  153,
+                  177
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area PFop (IPL)",
+                    "gray": "114"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hOc4v (LingG)",
+              "num": "114",
+              "id": "9",
+              "grayvalue": "115",
+              "iav": {
+                "name": "Area hOc4v (LingG)",
+                "labelIndex": 58,
+                "rgb": [
+                  222,
+                  77,
+                  155
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hOc4v (LingG)",
+                    "gray": "115"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area p32 (pACC)",
+              "num": "115",
+              "id": "47",
+              "grayvalue": "116",
+              "iav": {
+                "name": "Area p32 (pACC)",
+                "labelIndex": 113,
+                "rgb": [
+                  87,
+                  135,
+                  14
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area p32 (pACC)",
+                    "gray": "116"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hOc1 (V1, 17, CalcS)",
+              "num": "116",
+              "id": "8",
+              "grayvalue": "117",
+              "iav": {
+                "name": "Area hOc1 (V1, 17, CalcS)",
+                "labelIndex": 120,
+                "rgb": [
+                  190,
+                  132,
+                  147
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hOc1 (V1, 17, CalcS)",
+                    "gray": "117"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Fo2 (OFC)",
+              "num": "117",
+              "id": "4",
+              "grayvalue": "118",
+              "iav": {
+                "name": "Area Fo2 (OFC)",
+                "labelIndex": 67,
+                "rgb": [
+                  0,
+                  255,
+                  0
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Fo2 (OFC)",
+                    "gray": "118"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area i29 (retrosplenial)",
+              "num": "118",
+              "id": "265",
+              "grayvalue": "119"
+            },
+            {
+              "name": "Area hIP7 (IPS)",
+              "num": "119",
+              "id": "296",
+              "grayvalue": "120",
+              "iav": {
+                "name": "Area hIP7 (IPS)",
+                "labelIndex": 35,
+                "rgb": [
+                  71,
+                  196,
+                  218
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hIP7 (IPS)",
+                    "gray": "120"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area TE 2.2 (STG)",
+              "num": "120",
+              "id": "34",
+              "grayvalue": "121",
+              "iav": {
+                "name": "Area TE 2.2 (STG)",
+                "labelIndex": 93,
+                "rgb": [
+                  58,
+                  104,
+                  125
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area TE 2.2 (STG)",
+                    "gray": "119"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 7PC (SPL)",
+              "num": "121",
+              "id": "132",
+              "grayvalue": "122",
+              "iav": {
+                "name": "Area 7PC (SPL)",
+                "labelIndex": 4,
+                "rgb": [
+                  252,
+                  89,
+                  28
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 7PC (SPL)",
+                    "gray": "121"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hOc4lp (LOC)",
+              "num": "122",
+              "id": "117",
+              "grayvalue": "123",
+              "iav": {
+                "name": "Area hOc4lp (LOC)",
+                "labelIndex": 51,
+                "rgb": [
+                  96,
+                  113,
+                  253
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hOc4lp (LOC)",
+                    "gray": "122"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Fo3 (OFC)",
+              "num": "123",
+              "id": "5",
+              "grayvalue": "124",
+              "iav": {
+                "name": "Area Fo3 (OFC)",
+                "labelIndex": 48,
+                "rgb": [
+                  182,
+                  189,
+                  250
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Fo3 (OFC)",
+                    "gray": "123"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area TeI (STG)",
+              "num": "124",
+              "id": "242",
+              "grayvalue": "125",
+              "iav": {
+                "name": "Area TeI (STG)",
+                "labelIndex": 79,
+                "rgb": [
+                  23,
+                  78,
+                  92
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area TeI (STG)",
+                    "gray": "124"
+                  }
+                }
+              }
+            }
+          ]
+        },
+        {
+          "name": "left",
+          "children": [
+            {
+              "name": "Area TE 1.1 (HESCHL)",
+              "num": "0",
+              "id": "33",
+              "grayvalue": "1",
+              "iav": {
+                "name": "Area TE 1.1 (HESCHL)",
+                "labelIndex": 39,
+                "rgb": [
+                  8,
+                  113,
+                  68
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area TE 1.1 (HESCHL)",
+                    "gray": "1"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 3b (PostCG)",
+              "num": "1",
+              "id": "127",
+              "grayvalue": "2",
+              "iav": {
+                "name": "Area 3b (PostCG)",
+                "labelIndex": 37,
+                "rgb": [
+                  239,
+                  246,
+                  155
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 3b (PostCG)",
+                    "gray": "2"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hOc4la (LOC)",
+              "num": "2",
+              "id": "118",
+              "grayvalue": "3",
+              "iav": {
+                "name": "Area hOc4la (LOC)",
+                "labelIndex": 29,
+                "rgb": [
+                  233,
+                  168,
+                  189
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hOc4la (LOC)",
+                    "gray": "4"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area PF (IPL)",
+              "num": "3",
+              "id": "206",
+              "grayvalue": "4",
+              "iav": {
+                "name": "Area PF (IPL)",
+                "labelIndex": 12,
+                "rgb": [
+                  226,
+                  211,
+                  61
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area PF (IPL)",
+                    "gray": "5"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hOc2 (V2, 18)",
+              "num": "4",
+              "id": "7",
+              "grayvalue": "5",
+              "iav": {
+                "name": "Area hOc2 (V2, 18)",
+                "labelIndex": 21,
+                "rgb": [
+                  84,
+                  110,
+                  22
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hOc2 (V2, 18)",
+                    "gray": "6"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 6d2 (PreCG)",
+              "num": "5",
+              "id": "288",
+              "grayvalue": "6",
+              "iav": {
+                "name": "Area 6d2 (PreCG)",
+                "labelIndex": 82,
+                "rgb": [
+                  170,
+                  151,
+                  180
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 6d2 (PreCG)",
+                    "gray": "7"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Ig3 (Insula)",
+              "num": "6",
+              "id": "336",
+              "grayvalue": "7",
+              "iav": {
+                "name": "Area Ig3 (Insula)",
+                "labelIndex": 105,
+                "rgb": [
+                  105,
+                  253,
+                  197
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Ig3 (Insula)",
+                    "gray": "8"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hOc4d (Cuneus)",
+              "num": "7",
+              "id": "119",
+              "grayvalue": "8",
+              "iav": {
+                "name": "Area hOc4d (Cuneus)",
+                "labelIndex": 44,
+                "rgb": [
+                  109,
+                  218,
+                  10
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hOc4d (Cuneus)",
+                    "gray": "9"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Fo4 (OFC)",
+              "num": "8",
+              "id": "324",
+              "grayvalue": "9",
+              "iav": {
+                "name": "Area Fo4 (OFC)",
+                "labelIndex": 80,
+                "rgb": [
+                  163,
+                  204,
+                  53
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Fo4 (OFC)",
+                    "gray": "10"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area OP4 (POperc)",
+              "num": "9",
+              "id": "72",
+              "grayvalue": "10",
+              "iav": {
+                "name": "Area OP4 (POperc)",
+                "labelIndex": 14,
+                "rgb": [
+                  89,
+                  80,
+                  132
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area OP4 (POperc)",
+                    "gray": "11"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 1 (PostCG)",
+              "num": "10",
+              "id": "125",
+              "grayvalue": "11",
+              "iav": {
+                "name": "Area 1 (PostCG)",
+                "labelIndex": 116,
+                "rgb": [
+                  232,
+                  185,
+                  250
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 1 (PostCG)",
+                    "gray": "12"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Ch 4 (Basal Forebrain)",
+              "num": "11",
+              "id": "264",
+              "grayvalue": "12",
+              "iav": {
+                "name": "Ch 4 (Basal Forebrain)",
+                "labelIndex": 97,
+                "rgb": [
+                  200,
+                  200,
+                  200
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Ch 4 (Basal Forebrain)",
+                    "gray": "13"
+                  }
+                }
+              }
+            },
+            {
+              "name": "GapMap Frontal-I (GapMap)",
+              "num": "12",
+              "id": "500",
+              "grayvalue": "13",
+              "iav": {
+                "name": "Frontal-I (GapMap)",
+                "labelIndex": 71,
+                "rgb": [
+                  186,
+                  255,
+                  25
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Frontal-I (GapMap)",
+                    "gray": "14"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hPO1 (POS)",
+              "num": "13",
+              "id": "297",
+              "grayvalue": "14",
+              "iav": {
+                "name": "Area hPO1 (POS)",
+                "labelIndex": 69,
+                "rgb": [
+                  153,
+                  232,
+                  235
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hPO1 (POS)",
+                    "gray": "15"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Dorsal Dentate Nucleus (Cerebellum)",
+              "num": "14",
+              "id": "240",
+              "grayvalue": "15",
+              "iav": {
+                "name": "Dorsal Dentate Nucleus (Cerebellum)",
+                "labelIndex": 111,
+                "rgb": [
+                  200,
+                  200,
+                  200
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Dorsal Dentate Nucleus (Cerebellum)",
+                    "gray": "16"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area OP2 (POperc)",
+              "num": "15",
+              "id": "74",
+              "grayvalue": "16",
+              "iav": {
+                "name": "Area OP2 (POperc)",
+                "labelIndex": 122,
+                "rgb": [
+                  36,
+                  47,
+                  221
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area OP2 (POperc)",
+                    "gray": "17"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area TE 1.0 (HESCHL)",
+              "num": "16",
+              "id": "27",
+              "grayvalue": "17",
+              "iav": {
+                "name": "Area TE 1.0 (HESCHL)",
+                "labelIndex": 34,
+                "rgb": [
+                  252,
+                  84,
+                  222
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area TE 1.0 (HESCHL)",
+                    "gray": "18"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area STS2 (STS)",
+              "num": "17",
+              "id": "272",
+              "grayvalue": "18",
+              "iav": {
+                "name": "Area STS2 (STS)",
+                "labelIndex": 98,
+                "rgb": [
+                  62,
+                  117,
+                  123
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area STS2 (STS)",
+                    "gray": "19"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Id3 (Insula)",
+              "num": "18",
+              "id": "57",
+              "grayvalue": "19",
+              "iav": {
+                "name": "Area Id3 (Insula)",
+                "labelIndex": 54,
+                "rgb": [
+                  32,
+                  32,
+                  58
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Id3 (Insula)",
+                    "gray": "20"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 6mp (SMA, mesial SFG)",
+              "num": "19",
+              "id": "298",
+              "grayvalue": "20",
+              "iav": {
+                "name": "Area 6mp (SMA, mesial SFG)",
+                "labelIndex": 108,
+                "rgb": [
+                  75,
+                  95,
+                  87
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 6mp (SMA, mesial SFG)",
+                    "gray": "21"
+                  }
+                }
+              }
+            },
+            {
+              "name": "DG (Hippocampus)",
+              "num": "20",
+              "id": "61",
+              "grayvalue": "21",
+              "iav": {
+                "name": "DG (Hippocampus)",
+                "labelIndex": 91,
+                "rgb": [
+                  0,
+                  149,
+                  8
+                ],
+                "_": {
+                  "xml": {
+                    "name": "DG (Hippocampus)",
+                    "gray": "22"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area PGp (IPL)",
+              "num": "21",
+              "id": "108",
+              "grayvalue": "22",
+              "iav": {
+                "name": "Area PGp (IPL)",
+                "labelIndex": 65,
+                "rgb": [
+                  92,
+                  116,
+                  83
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area PGp (IPL)",
+                    "gray": "23"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Ig1 (Insula)",
+              "num": "22",
+              "id": "115",
+              "grayvalue": "23",
+              "iav": {
+                "name": "Area Ig1 (Insula)",
+                "labelIndex": 25,
+                "rgb": [
+                  18,
+                  111,
+                  40
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Ig1 (Insula)",
+                    "gray": "24"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 6ma (preSMA, mesial SFG)",
+              "num": "23",
+              "id": "299",
+              "grayvalue": "24",
+              "iav": {
+                "name": "Area 6ma (preSMA, mesial SFG)",
+                "labelIndex": 70,
+                "rgb": [
+                  204,
+                  108,
+                  222
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 6ma (preSMA, mesial SFG)",
+                    "gray": "25"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area TI (STG)",
+              "num": "24",
+              "id": "243",
+              "grayvalue": "25",
+              "iav": {
+                "name": "Area TI (STG)",
+                "labelIndex": 13,
+                "rgb": [
+                  86,
+                  101,
+                  22
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area TI (STG)",
+                    "gray": "26"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area PFt (IPL)",
+              "num": "25",
+              "id": "109",
+              "grayvalue": "26",
+              "iav": {
+                "name": "Area PFt (IPL)",
+                "labelIndex": 20,
+                "rgb": [
+                  120,
+                  135,
+                  232
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area PFt (IPL)",
+                    "gray": "27"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area p24ab (pACC)",
+              "num": "26",
+              "id": "231",
+              "grayvalue": "27",
+              "iav": {
+                "name": "Area p24ab (pACC)",
+                "labelIndex": 63,
+                "rgb": [
+                  153,
+                  195,
+                  229
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area p24ab (pACC)",
+                    "gray": "28"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area p24c (pACC)",
+              "num": "27",
+              "id": "232",
+              "grayvalue": "28",
+              "iav": {
+                "name": "Area p24c (pACC)",
+                "labelIndex": 57,
+                "rgb": [
+                  241,
+                  164,
+                  195
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area p24c (pACC)",
+                    "gray": "29"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hOc3v (LingG)",
+              "num": "28",
+              "id": "10",
+              "grayvalue": "29",
+              "iav": {
+                "name": "Area hOc3v (LingG)",
+                "labelIndex": 77,
+                "rgb": [
+                  83,
+                  179,
+                  155
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hOc3v (LingG)",
+                    "gray": "30"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area TE 3 (STG)",
+              "num": "29",
+              "id": "31",
+              "grayvalue": "30",
+              "iav": {
+                "name": "Area TE 3 (STG)",
+                "labelIndex": 15,
+                "rgb": [
+                  159,
+                  104,
+                  108
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area TE 3 (STG)",
+                    "gray": "31"
+                  }
+                }
+              }
+            },
+            {
+              "name": "GapMap Frontal-to-Occipital (GapMap)",
+              "num": "30",
+              "id": "504",
+              "grayvalue": "31",
+              "iav": {
+                "name": "Frontal-to-Occipital (GapMap)",
+                "labelIndex": 33,
+                "rgb": [
+                  104,
+                  161,
+                  93
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Frontal-to-Occipital (GapMap)",
+                    "gray": "33"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area PFcm (IPL)",
+              "num": "31",
+              "id": "113",
+              "grayvalue": "32",
+              "iav": {
+                "name": "Area PFcm (IPL)",
+                "labelIndex": 103,
+                "rgb": [
+                  98,
+                  128,
+                  120
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area PFcm (IPL)",
+                    "gray": "32"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 7P (SPL)",
+              "num": "32",
+              "id": "208",
+              "grayvalue": "33",
+              "iav": {
+                "name": "Area 7P (SPL)",
+                "labelIndex": 24,
+                "rgb": [
+                  52,
+                  20,
+                  106
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 7P (SPL)",
+                    "gray": "35"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hIP5 (IPS)",
+              "num": "33",
+              "id": "295",
+              "grayvalue": "34",
+              "iav": {
+                "name": "Area hIP5 (IPS)",
+                "labelIndex": 7,
+                "rgb": [
+                  217,
+                  87,
+                  210
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hIP5 (IPS)",
+                    "gray": "34"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 6d1 (PreCG)",
+              "num": "34",
+              "id": "287",
+              "grayvalue": "35",
+              "iav": {
+                "name": "Area 6d1 (PreCG)",
+                "labelIndex": 90,
+                "rgb": [
+                  45,
+                  33,
+                  27
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 6d1 (PreCG)",
+                    "gray": "36"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area FG1 (FusG)",
+              "num": "35",
+              "id": "107",
+              "grayvalue": "36",
+              "iav": {
+                "name": "Area FG1 (FusG)",
+                "labelIndex": 84,
+                "rgb": [
+                  131,
+                  183,
+                  58
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area FG1 (FusG)",
+                    "gray": "37"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hIP4 (IPS)",
+              "num": "36",
+              "id": "294",
+              "grayvalue": "37",
+              "iav": {
+                "name": "Area hIP4 (IPS)",
+                "labelIndex": 115,
+                "rgb": [
+                  254,
+                  52,
+                  184
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hIP4 (IPS)",
+                    "gray": "38"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area OP6 (Frontal Operculum)",
+              "num": "37",
+              "id": "349",
+              "grayvalue": "38",
+              "iav": {
+                "name": "Area OP6 (Frontal Operculum)",
+                "labelIndex": 31,
+                "rgb": [
+                  138,
+                  63,
+                  100
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area OP6 (Frontal Operculum)",
+                    "gray": "39"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area FG2 (FusG)",
+              "num": "38",
+              "id": "106",
+              "grayvalue": "39",
+              "iav": {
+                "name": "Area FG2 (FusG)",
+                "labelIndex": 28,
+                "rgb": [
+                  67,
+                  94,
+                  149
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area FG2 (FusG)",
+                    "gray": "40"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hIP6 (IPS)",
+              "num": "39",
+              "id": "292",
+              "grayvalue": "40",
+              "iav": {
+                "name": "Area hIP6 (IPS)",
+                "labelIndex": 95,
+                "rgb": [
+                  237,
+                  233,
+                  37
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hIP6 (IPS)",
+                    "gray": "41"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hIP3 (IPS)",
+              "num": "40",
+              "id": "133",
+              "grayvalue": "41",
+              "iav": {
+                "name": "Area hIP3 (IPS)",
+                "labelIndex": 75,
+                "rgb": [
+                  113,
+                  172,
+                  229
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hIP3 (IPS)",
+                    "gray": "43"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Fo5 (OFC)",
+              "num": "41",
+              "id": "325",
+              "grayvalue": "42",
+              "iav": {
+                "name": "Area Fo5 (OFC)",
+                "labelIndex": 76,
+                "rgb": [
+                  219,
+                  11,
+                  91
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Fo5 (OFC)",
+                    "gray": "42"
+                  }
+                }
+              }
+            },
+            {
+              "name": "CM (Amygdala)",
+              "num": "42",
+              "id": "290",
+              "grayvalue": "43",
+              "iav": {
+                "name": "CM (Amygdala)",
+                "labelIndex": 85,
+                "rgb": [
+                  200,
+                  200,
+                  200
+                ],
+                "_": {
+                  "xml": {
+                    "name": "CM (Amygdala)",
+                    "gray": "44"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area OP7 (Frontal Operculum)",
+              "num": "43",
+              "id": "350",
+              "grayvalue": "44",
+              "iav": {
+                "name": "Area OP7 (Frontal Operculum)",
+                "labelIndex": 60,
+                "rgb": [
+                  134,
+                  116,
+                  192
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area OP7 (Frontal Operculum)",
+                    "gray": "45"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 4p (PreCG)",
+              "num": "44",
+              "id": "123",
+              "grayvalue": "45",
+              "iav": {
+                "name": "Area 4p (PreCG)",
+                "labelIndex": 9,
+                "rgb": [
+                  116,
+                  92,
+                  124
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 4p (PreCG)",
+                    "gray": "46"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 33 (ACC)",
+              "num": "45",
+              "id": "39",
+              "grayvalue": "46",
+              "iav": {
+                "name": "Area 33 (ACC)",
+                "labelIndex": 96,
+                "rgb": [
+                  51,
+                  57,
+                  245
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 33 (ACC)",
+                    "gray": "47"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hOc6 (POS)",
+              "num": "46",
+              "id": "291",
+              "grayvalue": "47",
+              "iav": {
+                "name": "Area hOc6 (POS)",
+                "labelIndex": 72,
+                "rgb": [
+                  239,
+                  66,
+                  26
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hOc6 (POS)",
+                    "gray": "49"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Ig2 (Insula)",
+              "num": "47",
+              "id": "114",
+              "grayvalue": "48",
+              "iav": {
+                "name": "Area Ig2 (Insula)",
+                "labelIndex": 45,
+                "rgb": [
+                  105,
+                  61,
+                  82
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Ig2 (Insula)",
+                    "gray": "50"
+                  }
+                }
+              }
+            },
+            {
+              "name": "CA2 (Hippocampus)",
+              "num": "48",
+              "id": "58",
+              "grayvalue": "49",
+              "iav": {
+                "name": "CA2 (Hippocampus)",
+                "labelIndex": 23,
+                "rgb": [
+                  0,
+                  250,
+                  98
+                ],
+                "_": {
+                  "xml": {
+                    "name": "CA2 (Hippocampus)",
+                    "gray": "51"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Fp2 (FPole)",
+              "num": "49",
+              "id": "211",
+              "grayvalue": "50",
+              "iav": {
+                "name": "Area Fp2 (FPole)",
+                "labelIndex": 101,
+                "rgb": [
+                  239,
+                  137,
+                  211
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Fp2 (FPole)",
+                    "gray": "52"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Entorhinal Cortex",
+              "num": "50",
+              "id": "60",
+              "grayvalue": "51",
+              "iav": {
+                "name": "Entorhinal Cortex",
+                "labelIndex": 64,
+                "rgb": [
+                  234,
+                  239,
+                  14
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Entorhinal Cortex",
+                    "gray": "53"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Id4 (Insula)",
+              "num": "51",
+              "id": "337",
+              "grayvalue": "52",
+              "iav": {
+                "name": "Area Id4 (Insula)",
+                "labelIndex": 53,
+                "rgb": [
+                  38,
+                  174,
+                  113
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Id4 (Insula)",
+                    "gray": "54"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Id2 (Insula)",
+              "num": "52",
+              "id": "56",
+              "grayvalue": "53",
+              "iav": {
+                "name": "Area Id2 (Insula)",
+                "labelIndex": 99,
+                "rgb": [
+                  225,
+                  126,
+                  73
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Id2 (Insula)",
+                    "gray": "55"
+                  }
+                }
+              }
+            },
+            {
+              "name": "CA1 (Hippocampus)",
+              "num": "53",
+              "id": "66",
+              "grayvalue": "54",
+              "iav": {
+                "name": "CA1 (Hippocampus)",
+                "labelIndex": 61,
+                "rgb": [
+                  250,
+                  89,
+                  0
+                ],
+                "_": {
+                  "xml": {
+                    "name": "CA1 (Hippocampus)",
+                    "gray": "57"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area OP1 (POperc)",
+              "num": "54",
+              "id": "73",
+              "grayvalue": "55",
+              "iav": {
+                "name": "Area OP1 (POperc)",
+                "labelIndex": 1,
+                "rgb": [
+                  250,
+                  182,
+                  34
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area OP1 (POperc)",
+                    "gray": "56"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Id6 (Insula)",
+              "num": "55",
+              "id": "340",
+              "grayvalue": "56",
+              "iav": {
+                "name": "Area Id6 (Insula)",
+                "labelIndex": 11,
+                "rgb": [
+                  138,
+                  127,
+                  119
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Id6 (Insula)",
+                    "gray": "58"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hIP8 (IPS)",
+              "num": "56",
+              "id": "293",
+              "grayvalue": "57",
+              "iav": {
+                "name": "Area hIP8 (IPS)",
+                "labelIndex": 50,
+                "rgb": [
+                  223,
+                  109,
+                  3
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hIP8 (IPS)",
+                    "gray": "60"
+                  }
+                }
+              }
+            },
+            {
+              "name": "IF (Amygdala)",
+              "num": "57",
+              "id": "237",
+              "grayvalue": "58",
+              "iav": {
+                "name": "IF (Amygdala)",
+                "labelIndex": 46,
+                "rgb": [
+                  200,
+                  200,
+                  200
+                ],
+                "_": {
+                  "xml": {
+                    "name": "IF (Amygdala)",
+                    "gray": "61"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area OP9 (Frontal Operculum)",
+              "num": "58",
+              "id": "274",
+              "grayvalue": "59",
+              "iav": {
+                "name": "Area OP9 (Frontal Operculum)",
+                "labelIndex": 117,
+                "rgb": [
+                  175,
+                  123,
+                  34
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area OP9 (Frontal Operculum)",
+                    "gray": "62"
+                  }
+                }
+              }
+            },
+            {
+              "name": "GapMap Temporal-to-Parietal (GapMap)",
+              "num": "59",
+              "id": "503",
+              "grayvalue": "60",
+              "iav": {
+                "name": "Temporal-to-Parietal (GapMap)",
+                "labelIndex": 2,
+                "rgb": [
+                  76,
+                  90,
+                  161
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Temporal-to-Parietal (GapMap)",
+                    "gray": "64"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 6d3 (SFS)",
+              "num": "60",
+              "id": "289",
+              "grayvalue": "61",
+              "iav": {
+                "name": "Area 6d3 (SFS)",
+                "labelIndex": 68,
+                "rgb": [
+                  55,
+                  239,
+                  21
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 6d3 (SFS)",
+                    "gray": "66"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Ventral Dentate Nucleus (Cerebellum)",
+              "num": "61",
+              "id": "241",
+              "grayvalue": "62",
+              "iav": {
+                "name": "Ventral Dentate Nucleus (Cerebellum)",
+                "labelIndex": 38,
+                "rgb": [
+                  200,
+                  200,
+                  200
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Ventral Dentate Nucleus (Cerebellum)",
+                    "gray": "65"
+                  }
+                }
+              }
+            },
+            {
+              "name": "HATA (Hippocampus)",
+              "num": "62",
+              "id": "68",
+              "grayvalue": "63",
+              "iav": {
+                "name": "HATA (Hippocampus)",
+                "labelIndex": 56,
+                "rgb": [
+                  137,
+                  12,
+                  73
+                ],
+                "_": {
+                  "xml": {
+                    "name": "HATA (Hippocampus)",
+                    "gray": "67"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hIP2 (IPS)",
+              "num": "63",
+              "id": "129",
+              "grayvalue": "64",
+              "iav": {
+                "name": "Area hIP2 (IPS)",
+                "labelIndex": 59,
+                "rgb": [
+                  127,
+                  245,
+                  203
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hIP2 (IPS)",
+                    "gray": "68"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 44 (IFG)",
+              "num": "64",
+              "id": "2",
+              "grayvalue": "65",
+              "iav": {
+                "name": "Area 44 (IFG)",
+                "labelIndex": 92,
+                "rgb": [
+                  54,
+                  74,
+                  75
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 44 (IFG)",
+                    "gray": "69"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Fo7 (OFC)",
+              "num": "65",
+              "id": "327",
+              "grayvalue": "66",
+              "iav": {
+                "name": "Area Fo7 (OFC)",
+                "labelIndex": 52,
+                "rgb": [
+                  64,
+                  211,
+                  186
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Fo7 (OFC)",
+                    "gray": "70"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area a29 (retrosplenial)",
+              "num": "66",
+              "id": "353",
+              "grayvalue": "67"
+            },
+            {
+              "name": "Area 45 (IFG)",
+              "num": "67",
+              "id": "1",
+              "grayvalue": "68",
+              "iav": {
+                "name": "Area 45 (IFG)",
+                "labelIndex": 112,
+                "rgb": [
+                  167,
+                  103,
+                  146
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 45 (IFG)",
+                    "gray": "71"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Id1 (Insula)",
+              "num": "68",
+              "id": "116",
+              "grayvalue": "69",
+              "iav": {
+                "name": "Area Id1 (Insula)",
+                "labelIndex": 78,
+                "rgb": [
+                  141,
+                  112,
+                  216
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Id1 (Insula)",
+                    "gray": "72"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 5Ci (SPL)",
+              "num": "69",
+              "id": "136",
+              "grayvalue": "70",
+              "iav": {
+                "name": "Area 5Ci (SPL)",
+                "labelIndex": 88,
+                "rgb": [
+                  79,
+                  242,
+                  146
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 5Ci (SPL)",
+                    "gray": "73"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Id5 (Insula)",
+              "num": "70",
+              "id": "338",
+              "grayvalue": "71",
+              "iav": {
+                "name": "Area Id5 (Insula)",
+                "labelIndex": 104,
+                "rgb": [
+                  112,
+                  6,
+                  50
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Id5 (Insula)",
+                    "gray": "75"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area TE 1.2 (HESCHL)",
+              "num": "71",
+              "id": "30",
+              "grayvalue": "72",
+              "iav": {
+                "name": "Area TE 1.2 (HESCHL)",
+                "labelIndex": 86,
+                "rgb": [
+                  202,
+                  251,
+                  192
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area TE 1.2 (HESCHL)",
+                    "gray": "74"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area PGa (IPL)",
+              "num": "72",
+              "id": "110",
+              "grayvalue": "73",
+              "iav": {
+                "name": "Area PGa (IPL)",
+                "labelIndex": 81,
+                "rgb": [
+                  42,
+                  236,
+                  131
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area PGa (IPL)",
+                    "gray": "76"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hIP1 (IPS)",
+              "num": "73",
+              "id": "128",
+              "grayvalue": "74",
+              "iav": {
+                "name": "Area hIP1 (IPS)",
+                "labelIndex": 32,
+                "rgb": [
+                  66,
+                  149,
+                  82
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hIP1 (IPS)",
+                    "gray": "77"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 2 (PostCS)",
+              "num": "74",
+              "id": "252",
+              "grayvalue": "75",
+              "iav": {
+                "name": "Area 2 (PostCS)",
+                "labelIndex": 49,
+                "rgb": [
+                  23,
+                  13,
+                  35
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 2 (PostCS)",
+                    "gray": "78"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Ch 123 (Basal Forebrain)",
+              "num": "75",
+              "id": "286",
+              "grayvalue": "76",
+              "iav": {
+                "name": "Ch 123 (Basal Forebrain)",
+                "labelIndex": 62,
+                "rgb": [
+                  200,
+                  200,
+                  200
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Ch 123 (Basal Forebrain)",
+                    "gray": "79"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area TE 2.1 (STG)",
+              "num": "76",
+              "id": "28",
+              "grayvalue": "77",
+              "iav": {
+                "name": "Area TE 2.1 (STG)",
+                "labelIndex": 19,
+                "rgb": [
+                  67,
+                  254,
+                  44
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area TE 2.1 (STG)",
+                    "gray": "82"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Ia (Insula)",
+              "num": "77",
+              "id": "339",
+              "grayvalue": "78",
+              "iav": {
+                "name": "Area Ia (Insula)",
+                "labelIndex": 118,
+                "rgb": [
+                  71,
+                  217,
+                  62
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Ia (Insula)",
+                    "gray": "80"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hOc3d (Cuneus)",
+              "num": "78",
+              "id": "120",
+              "grayvalue": "79",
+              "iav": {
+                "name": "Area hOc3d (Cuneus)",
+                "labelIndex": 106,
+                "rgb": [
+                  105,
+                  191,
+                  48
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hOc3d (Cuneus)",
+                    "gray": "81"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area OP3 (POperc)",
+              "num": "79",
+              "id": "75",
+              "grayvalue": "80",
+              "iav": {
+                "name": "Area OP3 (POperc)",
+                "labelIndex": 22,
+                "rgb": [
+                  58,
+                  122,
+                  80
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area OP3 (POperc)",
+                    "gray": "84"
+                  }
+                }
+              }
+            },
+            {
+              "name": "VTM (Amygdala)",
+              "num": "80",
+              "id": "228",
+              "grayvalue": "81",
+              "iav": {
+                "name": "VTM (Amygdala)",
+                "labelIndex": 109,
+                "rgb": [
+                  200,
+                  200,
+                  200
+                ],
+                "_": {
+                  "xml": {
+                    "name": "VTM (Amygdala)",
+                    "gray": "83"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 7A (SPL)",
+              "num": "81",
+              "id": "134",
+              "grayvalue": "82",
+              "iav": {
+                "name": "Area 7A (SPL)",
+                "labelIndex": 17,
+                "rgb": [
+                  38,
+                  204,
+                  19
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 7A (SPL)",
+                    "gray": "85"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area OP8 (Frontal Operculum)",
+              "num": "82",
+              "id": "273",
+              "grayvalue": "83",
+              "iav": {
+                "name": "Area OP8 (Frontal Operculum)",
+                "labelIndex": 40,
+                "rgb": [
+                  29,
+                  76,
+                  168
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area OP8 (Frontal Operculum)",
+                    "gray": "86"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Subiculum (Hippocampus)",
+              "num": "83",
+              "id": "192",
+              "grayvalue": "84"
+            },
+            {
+              "name": "Area p29 (retrosplenial)",
+              "num": "84",
+              "id": "268",
+              "grayvalue": "85"
+            },
+            {
+              "name": "Area 25 (sACC)",
+              "num": "85",
+              "id": "184",
+              "grayvalue": "86",
+              "iav": {
+                "name": "Area 25 (sACC)",
+                "labelIndex": 3,
+                "rgb": [
+                  170,
+                  68,
+                  220
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 25 (sACC)",
+                    "gray": "88"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area OP5 (Frontal Operculum)",
+              "num": "86",
+              "id": "348",
+              "grayvalue": "87",
+              "iav": {
+                "name": "Area OP5 (Frontal Operculum)",
+                "labelIndex": 43,
+                "rgb": [
+                  222,
+                  78,
+                  253
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area OP5 (Frontal Operculum)",
+                    "gray": "90"
+                  }
+                }
+              }
+            },
+            {
+              "name": "CA3 (Hippocampus)",
+              "num": "87",
+              "id": "59",
+              "grayvalue": "88",
+              "iav": {
+                "name": "CA3 (Hippocampus)",
+                "labelIndex": 119,
+                "rgb": [
+                  39,
+                  0,
+                  250
+                ],
+                "_": {
+                  "xml": {
+                    "name": "CA3 (Hippocampus)",
+                    "gray": "91"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area PFm (IPL)",
+              "num": "88",
+              "id": "112",
+              "grayvalue": "89",
+              "iav": {
+                "name": "Area PFm (IPL)",
+                "labelIndex": 102,
+                "rgb": [
+                  53,
+                  76,
+                  145
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area PFm (IPL)",
+                    "gray": "92"
+                  }
+                }
+              }
+            },
+            {
+              "name": "GapMap Frontal-II (GapMap)",
+              "num": "89",
+              "id": "501",
+              "grayvalue": "90",
+              "iav": {
+                "name": "Frontal-II (GapMap)",
+                "labelIndex": 27,
+                "rgb": [
+                  255,
+                  135,
+                  15
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Frontal-II (GapMap)",
+                    "gray": "93"
+                  }
+                }
+              }
+            },
+            {
+              "name": "LB (Amygdala)",
+              "num": "90",
+              "id": "187",
+              "grayvalue": "91",
+              "iav": {
+                "name": "LB (Amygdala)",
+                "labelIndex": 41,
+                "rgb": [
+                  200,
+                  200,
+                  200
+                ],
+                "_": {
+                  "xml": {
+                    "name": "LB (Amygdala)",
+                    "gray": "94"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 4a (PreCG)",
+              "num": "91",
+              "id": "124",
+              "grayvalue": "92",
+              "iav": {
+                "name": "Area 4a (PreCG)",
+                "labelIndex": 100,
+                "rgb": [
+                  118,
+                  239,
+                  183
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 4a (PreCG)",
+                    "gray": "95"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area STS1 (STS)",
+              "num": "92",
+              "id": "271",
+              "grayvalue": "93",
+              "iav": {
+                "name": "Area STS1 (STS)",
+                "labelIndex": 114,
+                "rgb": [
+                  205,
+                  228,
+                  4
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area STS1 (STS)",
+                    "gray": "96"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 5L (SPL)",
+              "num": "93",
+              "id": "130",
+              "grayvalue": "94",
+              "iav": {
+                "name": "Area 5L (SPL)",
+                "labelIndex": 83,
+                "rgb": [
+                  184,
+                  185,
+                  58
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 5L (SPL)",
+                    "gray": "98"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 5M (SPL)",
+              "num": "94",
+              "id": "131",
+              "grayvalue": "95",
+              "iav": {
+                "name": "Area 5M (SPL)",
+                "labelIndex": 26,
+                "rgb": [
+                  225,
+                  245,
+                  76
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 5M (SPL)",
+                    "gray": "97"
+                  }
+                }
+              }
+            },
+            {
+              "name": "GapMap Frontal-to-Temporal (GapMap)",
+              "num": "95",
+              "id": "502",
+              "grayvalue": "96",
+              "iav": {
+                "name": "Frontal-to-Temporal (GapMap)",
+                "labelIndex": 110,
+                "rgb": [
+                  161,
+                  24,
+                  74
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Frontal-to-Temporal (GapMap)",
+                    "gray": "100"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area FG3 (FusG)",
+              "num": "96",
+              "id": "239",
+              "grayvalue": "97",
+              "iav": {
+                "name": "Area FG3 (FusG)",
+                "labelIndex": 42,
+                "rgb": [
+                  120,
+                  147,
+                  37
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area FG3 (FusG)",
+                    "gray": "99"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area i30 (retrosplenial)",
+              "num": "97",
+              "id": "267",
+              "grayvalue": "98"
+            },
+            {
+              "name": "Area p30 (retrosplenial)",
+              "num": "98",
+              "id": "269",
+              "grayvalue": "99"
+            },
+            {
+              "name": "Area Fp1 (FPole)",
+              "num": "99",
+              "id": "212",
+              "grayvalue": "100",
+              "iav": {
+                "name": "Area Fp1 (FPole)",
+                "labelIndex": 107,
+                "rgb": [
+                  226,
+                  14,
+                  200
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Fp1 (FPole)",
+                    "gray": "101"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area a30 (retrosplenial)",
+              "num": "100",
+              "id": "354",
+              "grayvalue": "101"
+            },
+            {
+              "name": "Fastigial Nucleus (Cerebellum)",
+              "num": "101",
+              "id": "219",
+              "grayvalue": "102",
+              "iav": {
+                "name": "Fastigial Nucleus (Cerebellum)",
+                "labelIndex": 89,
+                "rgb": [
+                  200,
+                  200,
+                  200
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Fastigial Nucleus (Cerebellum)",
+                    "gray": "103"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area s32 (sACC)",
+              "num": "102",
+              "id": "46",
+              "grayvalue": "103",
+              "iav": {
+                "name": "Area s32 (sACC)",
+                "labelIndex": 74,
+                "rgb": [
+                  193,
+                  94,
+                  250
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area s32 (sACC)",
+                    "gray": "102"
+                  }
+                }
+              }
+            },
+            {
+              "name": "MF (Amygdala)",
+              "num": "103",
+              "id": "235",
+              "grayvalue": "104",
+              "iav": {
+                "name": "MF (Amygdala)",
+                "labelIndex": 123,
+                "rgb": [
+                  200,
+                  200,
+                  200
+                ],
+                "_": {
+                  "xml": {
+                    "name": "MF (Amygdala)",
+                    "gray": "104"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area 3a (PostCG)",
+              "num": "104",
+              "id": "126",
+              "grayvalue": "105",
+              "iav": {
+                "name": "Area 3a (PostCG)",
+                "labelIndex": 121,
+                "rgb": [
+                  187,
+                  133,
+                  50
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 3a (PostCG)",
+                    "gray": "105"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hOc5 (LOC)",
+              "num": "105",
+              "id": "6",
+              "grayvalue": "106",
+              "iav": {
+                "name": "Area hOc5 (LOC)",
+                "labelIndex": 16,
+                "rgb": [
+                  255,
+                  0,
+                  0
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hOc5 (LOC)",
+                    "gray": "106"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Id7 (Insula)",
+              "num": "106",
+              "id": "159",
+              "grayvalue": "107",
+              "iav": {
+                "name": "Area Id7 (Insula)",
+                "labelIndex": 18,
+                "rgb": [
+                  188,
+                  250,
+                  216
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Id7 (Insula)",
+                    "gray": "107"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area FG4 (FusG)",
+              "num": "107",
+              "id": "238",
+              "grayvalue": "108",
+              "iav": {
+                "name": "Area FG4 (FusG)",
+                "labelIndex": 55,
+                "rgb": [
+                  170,
+                  220,
+                  175
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area FG4 (FusG)",
+                    "gray": "108"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Interposed Nucleus (Cerebellum)",
+              "num": "108",
+              "id": "251",
+              "grayvalue": "109",
+              "iav": {
+                "name": "Interposed Nucleus (Cerebellum)",
+                "labelIndex": 30,
+                "rgb": [
+                  200,
+                  200,
+                  200
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Interposed Nucleus (Cerebellum)",
+                    "gray": "109"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Fo1 (OFC)",
+              "num": "109",
+              "id": "3",
+              "grayvalue": "110",
+              "iav": {
+                "name": "Area Fo1 (OFC)",
+                "labelIndex": 47,
+                "rgb": [
+                  7,
+                  255,
+                  179
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Fo1 (OFC)",
+                    "gray": "111"
+                  }
+                }
+              }
+            },
+            {
+              "name": "SF (Amygdala)",
+              "num": "110",
+              "id": "185",
+              "grayvalue": "111",
+              "iav": {
+                "name": "SF (Amygdala)",
+                "labelIndex": 5,
+                "rgb": [
+                  200,
+                  200,
+                  200
+                ],
+                "_": {
+                  "xml": {
+                    "name": "SF (Amygdala)",
+                    "gray": "110"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area s24 (sACC)",
+              "num": "111",
+              "id": "183",
+              "grayvalue": "112",
+              "iav": {
+                "name": "Area s24 (sACC)",
+                "labelIndex": 73,
+                "rgb": [
+                  133,
+                  34,
+                  201
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area s24 (sACC)",
+                    "gray": "112"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Fo6 (OFC)",
+              "num": "112",
+              "id": "326",
+              "grayvalue": "113",
+              "iav": {
+                "name": "Area Fo6 (OFC)",
+                "labelIndex": 36,
+                "rgb": [
+                  199,
+                  156,
+                  187
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Fo6 (OFC)",
+                    "gray": "113"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hOc4v (LingG)",
+              "num": "113",
+              "id": "9",
+              "grayvalue": "114",
+              "iav": {
+                "name": "Area hOc4v (LingG)",
+                "labelIndex": 58,
+                "rgb": [
+                  222,
+                  77,
+                  155
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hOc4v (LingG)",
+                    "gray": "115"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area PFop (IPL)",
+              "num": "114",
+              "id": "111",
+              "grayvalue": "115",
+              "iav": {
+                "name": "Area PFop (IPL)",
+                "labelIndex": 94,
+                "rgb": [
+                  146,
+                  153,
+                  177
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area PFop (IPL)",
+                    "gray": "114"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area p32 (pACC)",
+              "num": "115",
+              "id": "47",
+              "grayvalue": "116",
+              "iav": {
+                "name": "Area p32 (pACC)",
+                "labelIndex": 113,
+                "rgb": [
+                  87,
+                  135,
+                  14
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area p32 (pACC)",
+                    "gray": "116"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hOc1 (V1, 17, CalcS)",
+              "num": "116",
+              "id": "8",
+              "grayvalue": "117",
+              "iav": {
+                "name": "Area hOc1 (V1, 17, CalcS)",
+                "labelIndex": 120,
+                "rgb": [
+                  190,
+                  132,
+                  147
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hOc1 (V1, 17, CalcS)",
+                    "gray": "117"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Fo2 (OFC)",
+              "num": "117",
+              "id": "4",
+              "grayvalue": "118",
+              "iav": {
+                "name": "Area Fo2 (OFC)",
+                "labelIndex": 67,
+                "rgb": [
+                  0,
+                  255,
+                  0
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Fo2 (OFC)",
+                    "gray": "118"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hIP7 (IPS)",
+              "num": "118",
+              "id": "296",
+              "grayvalue": "119",
+              "iav": {
+                "name": "Area hIP7 (IPS)",
+                "labelIndex": 35,
+                "rgb": [
+                  71,
+                  196,
+                  218
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hIP7 (IPS)",
+                    "gray": "120"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area TE 2.2 (STG)",
+              "num": "119",
+              "id": "34",
+              "grayvalue": "120",
+              "iav": {
+                "name": "Area TE 2.2 (STG)",
+                "labelIndex": 93,
+                "rgb": [
+                  58,
+                  104,
+                  125
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area TE 2.2 (STG)",
+                    "gray": "119"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area i29 (retrosplenial)",
+              "num": "120",
+              "id": "265",
+              "grayvalue": "121"
+            },
+            {
+              "name": "Area 7PC (SPL)",
+              "num": "121",
+              "id": "132",
+              "grayvalue": "122",
+              "iav": {
+                "name": "Area 7PC (SPL)",
+                "labelIndex": 4,
+                "rgb": [
+                  252,
+                  89,
+                  28
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area 7PC (SPL)",
+                    "gray": "121"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area hOc4lp (LOC)",
+              "num": "122",
+              "id": "117",
+              "grayvalue": "123",
+              "iav": {
+                "name": "Area hOc4lp (LOC)",
+                "labelIndex": 51,
+                "rgb": [
+                  96,
+                  113,
+                  253
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area hOc4lp (LOC)",
+                    "gray": "122"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area TeI (STG)",
+              "num": "123",
+              "id": "242",
+              "grayvalue": "124",
+              "iav": {
+                "name": "Area TeI (STG)",
+                "labelIndex": 79,
+                "rgb": [
+                  23,
+                  78,
+                  92
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area TeI (STG)",
+                    "gray": "124"
+                  }
+                }
+              }
+            },
+            {
+              "name": "Area Fo3 (OFC)",
+              "num": "124",
+              "id": "5",
+              "grayvalue": "125",
+              "iav": {
+                "name": "Area Fo3 (OFC)",
+                "labelIndex": 48,
+                "rgb": [
+                  182,
+                  189,
+                  250
+                ],
+                "_": {
+                  "xml": {
+                    "name": "Area Fo3 (OFC)",
+                    "gray": "123"
+                  }
+                }
+              }
+            }
+          ]
+        }
+      ]
+    }
+  ]
+}
diff --git a/src/res/ext/swmAggregatedData.json.bak b/src/res/ext/swmAggregatedData.json.bak
deleted file mode 100644
index f533c04ffeffbb904171593612d83be433a5d281..0000000000000000000000000000000000000000
--- a/src/res/ext/swmAggregatedData.json.bak
+++ /dev/null
@@ -1,2868 +0,0 @@
-[
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_CAC-PrCu_0",
-      "regionName": [
-        {
-          "regionName": "lh_CAC-PrCu_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_CAC-PrCu_0",
-          "name": "Probabilistic Map for lh_CAC-PrCu_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_CAC-PrCu_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_CAC-PrCu_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_CMF-Op_0",
-      "regionName": [
-        {
-          "regionName": "lh_CMF-Op_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_CMF-Op_0",
-          "name": "Probabilistic Map for lh_CMF-Op_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_CMF-Op_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_CMF-Op_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_CMF-PoC_0",
-      "regionName": [
-        {
-          "regionName": "lh_CMF-PoC_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_CMF-PoC_0",
-          "name": "Probabilistic Map for lh_CMF-PoC_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_CMF-PoC_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_CMF-PoC_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_CMF-PrC_0",
-      "regionName": [
-        {
-          "regionName": "lh_CMF-PrC_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_CMF-PrC_0",
-          "name": "Probabilistic Map for lh_CMF-PrC_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_CMF-PrC_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_CMF-PrC_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    },
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_CMF-PrC_1",
-      "regionName": [
-        {
-          "regionName": "lh_CMF-PrC_1",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_CMF-PrC_1",
-          "name": "Probabilistic Map for lh_CMF-PrC_1",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_CMF-PrC_1",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_CMF-PrC_1.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_CMF-RMF_0",
-      "regionName": [
-        {
-          "regionName": "lh_CMF-RMF_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_CMF-RMF_0",
-          "name": "Probabilistic Map for lh_CMF-RMF_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_CMF-RMF_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_CMF-RMF_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_CMF-SF_0",
-      "regionName": [
-        {
-          "regionName": "lh_CMF-SF_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_CMF-SF_0",
-          "name": "Probabilistic Map for lh_CMF-SF_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_CMF-SF_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_CMF-SF_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_Fu-LO_0",
-      "regionName": [
-        {
-          "regionName": "lh_Fu-LO_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_Fu-LO_0",
-          "name": "Probabilistic Map for lh_Fu-LO_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_Fu-LO_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_Fu-LO_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_IC-PrCu_0",
-      "regionName": [
-        {
-          "regionName": "lh_IC-PrCu_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_IC-PrCu_0",
-          "name": "Probabilistic Map for lh_IC-PrCu_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_IC-PrCu_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_IC-PrCu_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_IP-IT_0",
-      "regionName": [
-        {
-          "regionName": "lh_IP-IT_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_IP-IT_0",
-          "name": "Probabilistic Map for lh_IP-IT_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_IP-IT_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_IP-IT_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_IP-LO_1",
-      "regionName": [
-        {
-          "regionName": "lh_IP-LO_1",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_IP-LO_1",
-          "name": "Probabilistic Map for lh_IP-LO_1",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_IP-LO_1",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_IP-LO_1.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_IP-MT_0",
-      "regionName": [
-        {
-          "regionName": "lh_IP-MT_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_IP-MT_0",
-          "name": "Probabilistic Map for lh_IP-MT_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_IP-MT_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_IP-MT_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_IP-SM_0",
-      "regionName": [
-        {
-          "regionName": "lh_IP-SM_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_IP-SM_0",
-          "name": "Probabilistic Map for lh_IP-SM_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_IP-SM_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_IP-SM_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_IP-SP_0",
-      "regionName": [
-        {
-          "regionName": "lh_IP-SP_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_IP-SP_0",
-          "name": "Probabilistic Map for lh_IP-SP_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_IP-SP_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_IP-SP_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    },
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_IP-SP_1",
-      "regionName": [
-        {
-          "regionName": "lh_IP-SP_1",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_IP-SP_1",
-          "name": "Probabilistic Map for lh_IP-SP_1",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_IP-SP_1",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_IP-SP_1.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_IT-MT_0",
-      "regionName": [
-        {
-          "regionName": "lh_IT-MT_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_IT-MT_0",
-          "name": "Probabilistic Map for lh_IT-MT_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_IT-MT_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_IT-MT_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_LOF-Or_0",
-      "regionName": [
-        {
-          "regionName": "lh_LOF-Or_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_LOF-Or_0",
-          "name": "Probabilistic Map for lh_LOF-Or_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_LOF-Or_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_LOF-Or_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_LOF-RMF_0",
-      "regionName": [
-        {
-          "regionName": "lh_LOF-RMF_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_LOF-RMF_0",
-          "name": "Probabilistic Map for lh_LOF-RMF_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_LOF-RMF_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_LOF-RMF_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    },
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_LOF-RMF_1",
-      "regionName": [
-        {
-          "regionName": "lh_LOF-RMF_1",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_LOF-RMF_1",
-          "name": "Probabilistic Map for lh_LOF-RMF_1",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_LOF-RMF_1",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_LOF-RMF_1.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_LOF-ST_0",
-      "regionName": [
-        {
-          "regionName": "lh_LOF-ST_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_LOF-ST_0",
-          "name": "Probabilistic Map for lh_LOF-ST_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_LOF-ST_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_LOF-ST_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_MOF-ST_0",
-      "regionName": [
-        {
-          "regionName": "lh_MOF-ST_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_MOF-ST_0",
-          "name": "Probabilistic Map for lh_MOF-ST_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_MOF-ST_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_MOF-ST_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_MT-SM_0",
-      "regionName": [
-        {
-          "regionName": "lh_MT-SM_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_MT-SM_0",
-          "name": "Probabilistic Map for lh_MT-SM_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_MT-SM_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_MT-SM_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_MT-ST_0",
-      "regionName": [
-        {
-          "regionName": "lh_MT-ST_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_MT-ST_0",
-          "name": "Probabilistic Map for lh_MT-ST_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_MT-ST_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_MT-ST_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_Op-Ins_0",
-      "regionName": [
-        {
-          "regionName": "lh_Op-Ins_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_Op-Ins_0",
-          "name": "Probabilistic Map for lh_Op-Ins_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_Op-Ins_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_Op-Ins_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_Op-PrC_0",
-      "regionName": [
-        {
-          "regionName": "lh_Op-PrC_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_Op-PrC_0",
-          "name": "Probabilistic Map for lh_Op-PrC_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_Op-PrC_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_Op-PrC_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_Op-SF_0",
-      "regionName": [
-        {
-          "regionName": "lh_Op-SF_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_Op-SF_0",
-          "name": "Probabilistic Map for lh_Op-SF_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_Op-SF_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_Op-SF_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_Or-Ins_0",
-      "regionName": [
-        {
-          "regionName": "lh_Or-Ins_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_Or-Ins_0",
-          "name": "Probabilistic Map for lh_Or-Ins_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_Or-Ins_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_Or-Ins_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_PoC-Ins_0",
-      "regionName": [
-        {
-          "regionName": "lh_PoC-Ins_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_PoC-Ins_0",
-          "name": "Probabilistic Map for lh_PoC-Ins_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_PoC-Ins_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_PoC-Ins_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_PoCi-PrCu_0",
-      "regionName": [
-        {
-          "regionName": "lh_PoCi-PrCu_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_PoCi-PrCu_0",
-          "name": "Probabilistic Map for lh_PoCi-PrCu_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_PoCi-PrCu_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_PoCi-PrCu_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    },
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_PoCi-PrCu_1",
-      "regionName": [
-        {
-          "regionName": "lh_PoCi-PrCu_1",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_PoCi-PrCu_1",
-          "name": "Probabilistic Map for lh_PoCi-PrCu_1",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_PoCi-PrCu_1",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_PoCi-PrCu_1.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_PoCi-RAC_0",
-      "regionName": [
-        {
-          "regionName": "lh_PoCi-RAC_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_PoCi-RAC_0",
-          "name": "Probabilistic Map for lh_PoCi-RAC_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_PoCi-RAC_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_PoCi-RAC_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_PoCi-SF_0",
-      "regionName": [
-        {
-          "regionName": "lh_PoCi-SF_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_PoCi-SF_0",
-          "name": "Probabilistic Map for lh_PoCi-SF_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_PoCi-SF_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_PoCi-SF_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_PoC-PrC_0",
-      "regionName": [
-        {
-          "regionName": "lh_PoC-PrC_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_PoC-PrC_0",
-          "name": "Probabilistic Map for lh_PoC-PrC_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_PoC-PrC_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_PoC-PrC_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    },
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_PoC-PrC_1",
-      "regionName": [
-        {
-          "regionName": "lh_PoC-PrC_1",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_PoC-PrC_1",
-          "name": "Probabilistic Map for lh_PoC-PrC_1",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_PoC-PrC_1",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_PoC-PrC_1.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    },
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_PoC-PrC_2",
-      "regionName": [
-        {
-          "regionName": "lh_PoC-PrC_2",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_PoC-PrC_2",
-          "name": "Probabilistic Map for lh_PoC-PrC_2",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_PoC-PrC_2",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_PoC-PrC_2.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    },
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_PoC-PrC_3",
-      "regionName": [
-        {
-          "regionName": "lh_PoC-PrC_3",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_PoC-PrC_3",
-          "name": "Probabilistic Map for lh_PoC-PrC_3",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_PoC-PrC_3",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_PoC-PrC_3.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_PoC-SM_0",
-      "regionName": [
-        {
-          "regionName": "lh_PoC-SM_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_PoC-SM_0",
-          "name": "Probabilistic Map for lh_PoC-SM_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_PoC-SM_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_PoC-SM_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    },
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_PoC-SM_1",
-      "regionName": [
-        {
-          "regionName": "lh_PoC-SM_1",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_PoC-SM_1",
-          "name": "Probabilistic Map for lh_PoC-SM_1",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_PoC-SM_1",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_PoC-SM_1.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_PrC-Ins_0",
-      "regionName": [
-        {
-          "regionName": "lh_PrC-Ins_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_PrC-Ins_0",
-          "name": "Probabilistic Map for lh_PrC-Ins_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_PrC-Ins_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_PrC-Ins_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_PrC-SF_0",
-      "regionName": [
-        {
-          "regionName": "lh_PrC-SF_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_PrC-SF_0",
-          "name": "Probabilistic Map for lh_PrC-SF_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_PrC-SF_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_PrC-SF_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_PrC-SM_0",
-      "regionName": [
-        {
-          "regionName": "lh_PrC-SM_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_PrC-SM_0",
-          "name": "Probabilistic Map for lh_PrC-SM_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_PrC-SM_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_PrC-SM_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_RAC-SF_1",
-      "regionName": [
-        {
-          "regionName": "lh_RAC-SF_1",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_RAC-SF_1",
-          "name": "Probabilistic Map for lh_RAC-SF_1",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_RAC-SF_1",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_RAC-SF_1.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_RMF-SF_0",
-      "regionName": [
-        {
-          "regionName": "lh_RMF-SF_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_RMF-SF_0",
-          "name": "Probabilistic Map for lh_RMF-SF_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_RMF-SF_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_RMF-SF_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    },
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_RMF-SF_1",
-      "regionName": [
-        {
-          "regionName": "lh_RMF-SF_1",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_RMF-SF_1",
-          "name": "Probabilistic Map for lh_RMF-SF_1",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_RMF-SF_1",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_RMF-SF_1.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_SM-Ins_0",
-      "regionName": [
-        {
-          "regionName": "lh_SM-Ins_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_SM-Ins_0",
-          "name": "Probabilistic Map for lh_SM-Ins_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_SM-Ins_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_SM-Ins_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_SP-SM_0",
-      "regionName": [
-        {
-          "regionName": "lh_SP-SM_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_SP-SM_0",
-          "name": "Probabilistic Map for lh_SP-SM_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_SP-SM_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_SP-SM_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_ST-Ins_0",
-      "regionName": [
-        {
-          "regionName": "lh_ST-Ins_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_ST-Ins_0",
-          "name": "Probabilistic Map for lh_ST-Ins_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_ST-Ins_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_ST-Ins_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_ST-TT_0",
-      "regionName": [
-        {
-          "regionName": "lh_ST-TT_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_ST-TT_0",
-          "name": "Probabilistic Map for lh_ST-TT_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_ST-TT_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_ST-TT_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_Tr-Ins_0",
-      "regionName": [
-        {
-          "regionName": "lh_Tr-Ins_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_Tr-Ins_0",
-          "name": "Probabilistic Map for lh_Tr-Ins_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_Tr-Ins_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_Tr-Ins_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for lh_Tr-SF_0",
-      "regionName": [
-        {
-          "regionName": "lh_Tr-SF_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for lh_Tr-SF_0",
-          "name": "Probabilistic Map for lh_Tr-SF_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "lh_Tr-SF_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/left-hemisphere/Probability_Maps/lh_Tr-SF_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_CAC-PoCi_0",
-      "regionName": [
-        {
-          "regionName": "rh_CAC-PoCi_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_CAC-PoCi_0",
-          "name": "Probabilistic Map for rh_CAC-PoCi_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_CAC-PoCi_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_CAC-PoCi_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_CAC-PrCu_0",
-      "regionName": [
-        {
-          "regionName": "rh_CAC-PrCu_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_CAC-PrCu_0",
-          "name": "Probabilistic Map for rh_CAC-PrCu_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_CAC-PrCu_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_CAC-PrCu_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_CMF-PrC_0",
-      "regionName": [
-        {
-          "regionName": "rh_CMF-PrC_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_CMF-PrC_0",
-          "name": "Probabilistic Map for rh_CMF-PrC_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_CMF-PrC_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_CMF-PrC_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    },
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_CMF-PrC_1",
-      "regionName": [
-        {
-          "regionName": "rh_CMF-PrC_1",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_CMF-PrC_1",
-          "name": "Probabilistic Map for rh_CMF-PrC_1",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_CMF-PrC_1",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_CMF-PrC_1.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_CMF-RMF_0",
-      "regionName": [
-        {
-          "regionName": "rh_CMF-RMF_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_CMF-RMF_0",
-          "name": "Probabilistic Map for rh_CMF-RMF_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_CMF-RMF_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_CMF-RMF_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_CMF-SF_0",
-      "regionName": [
-        {
-          "regionName": "rh_CMF-SF_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_CMF-SF_0",
-          "name": "Probabilistic Map for rh_CMF-SF_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_CMF-SF_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_CMF-SF_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_CMF-SF_1",
-      "regionName": [
-        {
-          "regionName": "rh_CMF-SF_1",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_CMF-SF_1",
-          "name": "Probabilistic Map for rh_CMF-SF_1",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_CMF-SF_1",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_CMF-SF_1.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_Cu-Li_0",
-      "regionName": [
-        {
-          "regionName": "rh_Cu-Li_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_Cu-Li_0",
-          "name": "Probabilistic Map for rh_Cu-Li_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_Cu-Li_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_Cu-Li_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_Fu-LO_1",
-      "regionName": [
-        {
-          "regionName": "rh_Fu-LO_1",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_Fu-LO_1",
-          "name": "Probabilistic Map for rh_Fu-LO_1",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_Fu-LO_1",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_Fu-LO_1.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_IC-PrCu_0",
-      "regionName": [
-        {
-          "regionName": "rh_IC-PrCu_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_IC-PrCu_0",
-          "name": "Probabilistic Map for rh_IC-PrCu_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_IC-PrCu_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_IC-PrCu_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_IP-IT_0",
-      "regionName": [
-        {
-          "regionName": "rh_IP-IT_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_IP-IT_0",
-          "name": "Probabilistic Map for rh_IP-IT_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_IP-IT_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_IP-IT_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_IP-LO_0",
-      "regionName": [
-        {
-          "regionName": "rh_IP-LO_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_IP-LO_0",
-          "name": "Probabilistic Map for rh_IP-LO_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_IP-LO_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_IP-LO_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_IP-MT_0",
-      "regionName": [
-        {
-          "regionName": "rh_IP-MT_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_IP-MT_0",
-          "name": "Probabilistic Map for rh_IP-MT_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_IP-MT_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_IP-MT_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_IP-SM_0",
-      "regionName": [
-        {
-          "regionName": "rh_IP-SM_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_IP-SM_0",
-          "name": "Probabilistic Map for rh_IP-SM_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_IP-SM_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_IP-SM_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_IP-SP_0",
-      "regionName": [
-        {
-          "regionName": "rh_IP-SP_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_IP-SP_0",
-          "name": "Probabilistic Map for rh_IP-SP_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_IP-SP_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_IP-SP_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_IT-MT_1",
-      "regionName": [
-        {
-          "regionName": "rh_IT-MT_1",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_IT-MT_1",
-          "name": "Probabilistic Map for rh_IT-MT_1",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_IT-MT_1",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_IT-MT_1.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    },
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_IT-MT_2",
-      "regionName": [
-        {
-          "regionName": "rh_IT-MT_2",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_IT-MT_2",
-          "name": "Probabilistic Map for rh_IT-MT_2",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_IT-MT_2",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_IT-MT_2.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_LOF-MOF_0",
-      "regionName": [
-        {
-          "regionName": "rh_LOF-MOF_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_LOF-MOF_0",
-          "name": "Probabilistic Map for rh_LOF-MOF_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_LOF-MOF_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_LOF-MOF_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_LOF-RMF_0",
-      "regionName": [
-        {
-          "regionName": "rh_LOF-RMF_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_LOF-RMF_0",
-          "name": "Probabilistic Map for rh_LOF-RMF_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_LOF-RMF_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_LOF-RMF_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    },
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_LOF-RMF_1",
-      "regionName": [
-        {
-          "regionName": "rh_LOF-RMF_1",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_LOF-RMF_1",
-          "name": "Probabilistic Map for rh_LOF-RMF_1",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_LOF-RMF_1",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_LOF-RMF_1.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_LOF-ST_0",
-      "regionName": [
-        {
-          "regionName": "rh_LOF-ST_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_LOF-ST_0",
-          "name": "Probabilistic Map for rh_LOF-ST_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_LOF-ST_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_LOF-ST_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_LO-SP_0",
-      "regionName": [
-        {
-          "regionName": "rh_LO-SP_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_LO-SP_0",
-          "name": "Probabilistic Map for rh_LO-SP_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_LO-SP_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_LO-SP_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_MOF-ST_0",
-      "regionName": [
-        {
-          "regionName": "rh_MOF-ST_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_MOF-ST_0",
-          "name": "Probabilistic Map for rh_MOF-ST_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_MOF-ST_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_MOF-ST_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_MT-SM_0",
-      "regionName": [
-        {
-          "regionName": "rh_MT-SM_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_MT-SM_0",
-          "name": "Probabilistic Map for rh_MT-SM_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_MT-SM_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_MT-SM_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_MT-ST_0",
-      "regionName": [
-        {
-          "regionName": "rh_MT-ST_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_MT-ST_0",
-          "name": "Probabilistic Map for rh_MT-ST_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_MT-ST_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_MT-ST_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_Op-Ins_0",
-      "regionName": [
-        {
-          "regionName": "rh_Op-Ins_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_Op-Ins_0",
-          "name": "Probabilistic Map for rh_Op-Ins_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_Op-Ins_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_Op-Ins_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_Op-PrC_0",
-      "regionName": [
-        {
-          "regionName": "rh_Op-PrC_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_Op-PrC_0",
-          "name": "Probabilistic Map for rh_Op-PrC_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_Op-PrC_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_Op-PrC_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_Op-SF_0",
-      "regionName": [
-        {
-          "regionName": "rh_Op-SF_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_Op-SF_0",
-          "name": "Probabilistic Map for rh_Op-SF_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_Op-SF_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_Op-SF_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_Op-Tr_0",
-      "regionName": [
-        {
-          "regionName": "rh_Op-Tr_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_Op-Tr_0",
-          "name": "Probabilistic Map for rh_Op-Tr_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_Op-Tr_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_Op-Tr_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_Or-Ins_0",
-      "regionName": [
-        {
-          "regionName": "rh_Or-Ins_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_Or-Ins_0",
-          "name": "Probabilistic Map for rh_Or-Ins_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_Or-Ins_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_Or-Ins_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_PoCi-PrCu_1",
-      "regionName": [
-        {
-          "regionName": "rh_PoCi-PrCu_1",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_PoCi-PrCu_1",
-          "name": "Probabilistic Map for rh_PoCi-PrCu_1",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_PoCi-PrCu_1",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_PoCi-PrCu_1.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    },
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_PoCi-PrCu_2",
-      "regionName": [
-        {
-          "regionName": "rh_PoCi-PrCu_2",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_PoCi-PrCu_2",
-          "name": "Probabilistic Map for rh_PoCi-PrCu_2",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_PoCi-PrCu_2",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_PoCi-PrCu_2.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_PoCi-RAC_0",
-      "regionName": [
-        {
-          "regionName": "rh_PoCi-RAC_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_PoCi-RAC_0",
-          "name": "Probabilistic Map for rh_PoCi-RAC_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_PoCi-RAC_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_PoCi-RAC_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_PoC-PrC_0",
-      "regionName": [
-        {
-          "regionName": "rh_PoC-PrC_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_PoC-PrC_0",
-          "name": "Probabilistic Map for rh_PoC-PrC_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_PoC-PrC_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_PoC-PrC_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    },
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_PoC-PrC_1",
-      "regionName": [
-        {
-          "regionName": "rh_PoC-PrC_1",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_PoC-PrC_1",
-          "name": "Probabilistic Map for rh_PoC-PrC_1",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_PoC-PrC_1",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_PoC-PrC_1.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    },
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_PoC-PrC_2",
-      "regionName": [
-        {
-          "regionName": "rh_PoC-PrC_2",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_PoC-PrC_2",
-          "name": "Probabilistic Map for rh_PoC-PrC_2",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_PoC-PrC_2",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_PoC-PrC_2.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_PoC-SM_0",
-      "regionName": [
-        {
-          "regionName": "rh_PoC-SM_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_PoC-SM_0",
-          "name": "Probabilistic Map for rh_PoC-SM_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_PoC-SM_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_PoC-SM_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_PoC-SP_0",
-      "regionName": [
-        {
-          "regionName": "rh_PoC-SP_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_PoC-SP_0",
-          "name": "Probabilistic Map for rh_PoC-SP_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_PoC-SP_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_PoC-SP_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    },
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_PoC-SP_1",
-      "regionName": [
-        {
-          "regionName": "rh_PoC-SP_1",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_PoC-SP_1",
-          "name": "Probabilistic Map for rh_PoC-SP_1",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_PoC-SP_1",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_PoC-SP_1.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_PrC-Ins_0",
-      "regionName": [
-        {
-          "regionName": "rh_PrC-Ins_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_PrC-Ins_0",
-          "name": "Probabilistic Map for rh_PrC-Ins_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_PrC-Ins_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_PrC-Ins_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_PrC-SM_0",
-      "regionName": [
-        {
-          "regionName": "rh_PrC-SM_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_PrC-SM_0",
-          "name": "Probabilistic Map for rh_PrC-SM_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_PrC-SM_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_PrC-SM_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_PrC-SP_0",
-      "regionName": [
-        {
-          "regionName": "rh_PrC-SP_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_PrC-SP_0",
-          "name": "Probabilistic Map for rh_PrC-SP_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_PrC-SP_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_PrC-SP_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_RAC-SF_0",
-      "regionName": [
-        {
-          "regionName": "rh_RAC-SF_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_RAC-SF_0",
-          "name": "Probabilistic Map for rh_RAC-SF_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_RAC-SF_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_RAC-SF_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_RMF-SF_0",
-      "regionName": [
-        {
-          "regionName": "rh_RMF-SF_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_RMF-SF_0",
-          "name": "Probabilistic Map for rh_RMF-SF_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_RMF-SF_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_RMF-SF_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    },
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_RMF-SF_1",
-      "regionName": [
-        {
-          "regionName": "rh_RMF-SF_1",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_RMF-SF_1",
-          "name": "Probabilistic Map for rh_RMF-SF_1",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_RMF-SF_1",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_RMF-SF_1.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_SM-Ins_0",
-      "regionName": [
-        {
-          "regionName": "rh_SM-Ins_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_SM-Ins_0",
-          "name": "Probabilistic Map for rh_SM-Ins_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_SM-Ins_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_SM-Ins_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_SP-SM_0",
-      "regionName": [
-        {
-          "regionName": "rh_SP-SM_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_SP-SM_0",
-          "name": "Probabilistic Map for rh_SP-SM_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_SP-SM_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_SP-SM_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_ST-TT_0",
-      "regionName": [
-        {
-          "regionName": "rh_ST-TT_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_ST-TT_0",
-          "name": "Probabilistic Map for rh_ST-TT_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_ST-TT_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_ST-TT_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_Tr-Ins_0",
-      "regionName": [
-        {
-          "regionName": "rh_Tr-Ins_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_Tr-Ins_0",
-          "name": "Probabilistic Map for rh_Tr-Ins_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_Tr-Ins_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_Tr-Ins_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ],
-  [
-    {
-      "type": "Probabilistic Map",
-      "name": "Probabilistic Map for rh_Tr-SF_0",
-      "regionName": [
-        {
-          "regionName": "rh_Tr-SF_0",
-          "relationship": "equals"
-        }
-      ],
-      "files": [
-        {
-          "filename": "Probabilistic Map for rh_Tr-SF_0",
-          "name": "Probabilistic Map for rh_Tr-SF_0",
-          "mimetype": "application/nifti",
-          "properties": {
-            "name": null,
-            "description": "",
-            "publications": [],
-            "associatedRegion": "rh_Tr-SF_0",
-            "associatedParcellation": "Fibre Bundle Atlas - Short Bundle",
-            "datasetInfo": "MNIColin27_PMap.json"
-          },
-          "url": "https://neuroglancer-dev.humanbrainproject.org/precomputed/Fiber_Bundle/SWM_atlas/right-hemisphere/Probability_Maps/rh_Tr-SF_0.nii"
-        }
-      ],
-      "targetParcellation": "Fibre Bundle Atlas - Short Bundle"
-    }
-  ]
-]
\ No newline at end of file
diff --git a/src/res/ext/waxholmRatV2_0NehubaConfig.json b/src/res/ext/waxholmRatV2_0NehubaConfig.json
index e98b6ee833f6a6cb86c857e9114b0ec3d9294f09..e67574b32c71ad6bbb6c02b9dba01a92b4207ace 100644
--- a/src/res/ext/waxholmRatV2_0NehubaConfig.json
+++ b/src/res/ext/waxholmRatV2_0NehubaConfig.json
@@ -114,7 +114,7 @@
         },
         "v1_01": {
           "type": "segmentation",
-          "source": "precomputed://https://neuroglancer-dev.humanbrainproject.org/precomputed/WHS_SD_rat/parcellations/WHS_SD_rat_atlas_v1_01",
+          "source": "precomputed://https://neuroglancer.humanbrainproject.org/precomputed/WHS_SD_rat/parcellations/WHS_SD_rat_atlas_v1_01",
           "selectedAlpha": 0.35,
           "transform": [
             [
@@ -176,7 +176,7 @@
         },
         "v3": {
           "type": "segmentation",
-          "source": "precomputed://https://neuroglancer-dev.humanbrainproject.org/precomputed/WHS_SD_rat/parcellations/WHS_SD_rat_atlas_v3",
+          "source": "precomputed://https://neuroglancer.humanbrainproject.org/precomputed/WHS_SD_rat/parcellations/WHS_SD_rat_atlas_v3",
           "selectedAlpha": 0.35,
           "transform": [
             [
@@ -207,7 +207,7 @@
         },
         "WHS_SD_rat_atlas_v4_beta": {
           "type": "segmentation",
-          "source": "precomputed://https://neuroglancer-dev.humanbrainproject.org/precomputed/WHS_SD_rat/parcellations/WHS_SD_rat_atlas_v4_beta",
+          "source": "precomputed://https://neuroglancer.humanbrainproject.org/precomputed/WHS_SD_rat/parcellations/WHS_SD_rat_atlas_v4_beta",
           "selectedAlpha": 0.35,
           "transform": [
             [
diff --git a/src/res/favicons/favicon-128-dark.svg b/src/res/favicons/favicon-128-dark.svg
new file mode 100644
index 0000000000000000000000000000000000000000..49a9402c31fd7252b2dd3bc92e629d3a36d81a17
--- /dev/null
+++ b/src/res/favicons/favicon-128-dark.svg
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="128mm"
+   height="128mm"
+   viewBox="0 0 128 128"
+   version="1.1"
+   id="svg8"
+   inkscape:version="1.0.2 (394de47547, 2021-03-26)"
+   sodipodi:docname="favicon-128-dark.svg">
+  <defs
+     id="defs2" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1.4"
+     inkscape:cx="324.70471"
+     inkscape:cy="279.91944"
+     inkscape:document-units="mm"
+     inkscape:current-layer="layer2"
+     inkscape:document-rotation="0"
+     showgrid="false"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0"
+     inkscape:window-width="1853"
+     inkscape:window-height="1025"
+     inkscape:window-x="67"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1" />
+  <metadata
+     id="metadata5">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer2"
+     inkscape:label="main"
+     style="display:inline"
+     transform="translate(-79.414325,-20.601907)">
+    <path
+       style="fill:#ffffff;stroke-width:0.264583"
+       d="m 162.83944,128.89555 c -1.71263,-0.76119 -2.51354,-2.36516 -2.51354,-5.03388 0,-2.25335 0.78988,-3.77883 2.38125,-4.59886 2.34364,-1.20767 5.04289,-0.21921 5.96015,2.18259 0.5625,1.4729 0.4413,4.34887 -0.24282,5.76143 -0.83944,1.73329 -3.604,2.5692 -5.58504,1.68872 z m 3.49261,-1.63593 c 1.05602,-0.66537 1.47806,-3.54272 0.76867,-5.24053 -1.06024,-2.53752 -4.2164,-2.24958 -4.94489,0.45112 -0.36533,1.3544 -0.16041,3.36382 0.43584,4.27382 0.74822,1.14193 2.38765,1.36792 3.74038,0.51559 z m -39.87282,-3.28224 v -5.02708 h 3.3073 3.30729 v 0.66146 c 0,0.65913 -0.009,0.66145 -2.51354,0.66145 h -2.51355 v 1.45521 1.45521 h 2.24896 2.24896 v 0.79375 0.79375 h -2.24896 -2.24896 v 1.45521 1.45521 h 2.4915 c 2.49379,0 2.90743,0.15781 2.6006,0.99219 -0.0884,0.24024 -1.01868,0.33072 -3.40061,0.33072 h -3.27899 z m 7.9375,4.81268 c 0,-0.13636 0.65485,-1.20951 1.45521,-2.38478 0.80037,-1.17527 1.45521,-2.3002 1.45521,-2.49985 0,-0.19964 -0.60994,-1.24739 -1.35542,-2.32833 -1.81084,-2.62569 -1.8116,-2.62907 -0.59112,-2.61522 0.96613,0.011 1.07801,0.10373 2.1,1.74113 l 1.0795,1.72954 1.13439,-1.74113 c 1.02835,-1.57837 1.21805,-1.74112 2.02935,-1.74112 0.49223,0 0.89497,0.0976 0.89497,0.2168 0,0.11923 -0.45048,0.82058 -1.00107,1.55853 -0.55059,0.73796 -1.22383,1.75537 -1.49608,2.26091 l -0.495,0.91916 1.62837,2.43443 c 0.8956,1.33894 1.62836,2.48616 1.62836,2.54938 0,0.0632 -0.44834,0.11495 -0.99631,0.11495 -0.9347,0 -1.06576,-0.10637 -2.11905,-1.71979 -0.61751,-0.94588 -1.21097,-1.71645 -1.31879,-1.71237 -0.10783,0.004 -0.67231,0.74822 -1.25439,1.65365 -0.89834,1.39735 -1.18833,1.65876 -1.91823,1.72913 -0.47294,0.0456 -0.8599,-0.0286 -0.8599,-0.16502 z m 9.58531,-4.74653 0.072,-4.96094 2.36594,-0.0772 c 3.55309,-0.11592 4.9101,0.71487 4.9101,3.00606 0,2.39475 -0.96061,3.25698 -3.89065,3.49217 l -1.6656,0.1337 v 1.68357 1.68356 h -0.93188 -0.93188 z m 5.23136,-0.59532 c 0.52634,-0.52634 0.69769,-1.71985 0.32668,-2.27549 -0.36931,-0.55309 -1.35454,-0.89951 -2.55824,-0.89951 h -1.20802 v 1.85209 1.85208 h 1.45521 c 1.10243,0 1.58349,-0.12828 1.98437,-0.52917 z m 3.70417,0.51241 v -5.04385 l 0.85989,0.0829 0.8599,0.0829 0.0727,4.29948 0.0727,4.29948 h 2.2424 c 2.21551,0 2.24241,0.008 2.24241,0.66146 v 0.66145 h -3.175 -3.175 z m 17.99166,0.0168 v -5.02708 l 2.71198,0.001 c 2.22127,7.9e-4 2.86498,0.0943 3.55752,0.51654 1.45573,0.88762 1.51248,3.41795 0.10255,4.57238 l -0.73112,0.59863 0.8837,2.07396 c 0.48604,1.14069 0.88371,2.1305 0.88371,2.19958 0,0.0691 -0.38396,0.0883 -0.85324,0.0427 -0.77802,-0.0756 -0.91584,-0.23454 -1.56315,-1.8027 -0.91281,-2.21133 -1.12685,-2.42774 -2.30398,-2.32964 l -0.96817,0.0806 -0.0773,2.05052 -0.0773,2.05052 h -0.78261 -0.78263 v -5.02708 z m 5.1237,-1.07753 c 0.83208,-0.65453 0.88983,-1.37562 0.16797,-2.09748 -0.41157,-0.41157 -0.88194,-0.52917 -2.11667,-0.52917 h -1.5875 v 1.5875 1.5875 h 1.41953 c 1.03547,0 1.60814,-0.14836 2.11667,-0.54837 z m 4.4013,1.07752 v -5.02708 h 3.3073 3.30729 v 0.66146 c 0,0.65913 -0.009,0.66145 -2.51354,0.66145 h -2.51355 v 1.45521 1.45521 h 2.24896 2.24896 v 0.79375 0.79375 h -2.24896 -2.24896 v 1.45521 1.45521 h 2.51355 c 2.50472,0 2.51354,0.002 2.51354,0.66146 v 0.66145 h -3.30729 -3.3073 z m 8.73125,0 v -5.02708 h 2.75783 c 2.5548,0 2.81563,0.0486 3.543,0.66068 0.43184,0.36337 0.86108,0.96314 0.95387,1.33283 0.25133,1.00138 -0.29507,2.66624 -1.05728,3.22147 l -0.67115,0.4889 0.931,2.17514 0.931,2.17514 h -0.87632 c -0.83195,0 -0.914,-0.0904 -1.62079,-1.78593 -0.98454,-2.36185 -1.10215,-2.48856 -2.22268,-2.39463 l -0.94868,0.0795 -0.0773,2.05052 -0.0773,2.05052 h -0.78261 -0.78263 z m 4.96821,-0.94104 c 0.70292,-0.49234 0.77514,-1.58868 0.15016,-2.27927 -0.32907,-0.36362 -0.82218,-0.48386 -1.98437,-0.48386 h -1.5465 v 1.5875 1.5875 h 1.39634 c 0.83558,0 1.63249,-0.1654 1.98437,-0.41187 z m -97.572367,-9.5378 c -1.95888,-0.34634 -5.82084,-1.69103 -5.82084,-2.02677 0,-0.26055 1.56112,-3.71342 1.77614,-3.92844 0.17279,-0.17279 0.61281,-0.12624 1.29339,0.13682 1.5231,0.58871 4.90435,1.21598 6.554677,1.21598 2.60677,0 5.19386,-1.69122 5.19148,-3.39374 -0.003,-1.9815 -1.93734,-3.38013 -6.084437,-4.39883 -4.65894,-1.144439 -6.70269,-2.519669 -8.03124,-5.404209 -0.71141,-1.5446 -0.75135,-4.927329 -0.0761,-6.446859 0.70597,-1.588706 2.48912,-3.227144 4.27091,-3.924299 1.36156,-0.532739 2.0582,-0.621779 4.894797,-0.625631 3.42639,-0.0047 5.55454,0.402283 7.71765,1.475732 l 0.97077,0.481753 -0.70619,2.010008 c -0.80831,2.300676 -0.49127,2.212587 -4.26017,1.183737 -3.97503,-1.085119 -6.995447,-0.45827 -8.048857,1.670449 -0.41938,0.84746 -0.44249,1.10637 -0.15967,1.78914 0.64008,1.54529 2.27792,2.40158 6.602567,3.4519 5.57621,1.35429 8.19428,4.460389 7.88051,9.349499 -0.21116,3.29034 -1.90364,5.60538 -4.96953,6.79752 -1.17157,0.45555 -2.12561,0.57923 -4.89479,0.63451 -1.891777,0.0378 -3.737247,0.0161 -4.101047,-0.0483 z m 44.068567,-0.278 c -2.90689,-0.89207 -5.0522,-3.00939 -6.05006,-5.97114 -0.5442,-1.61522 -0.55975,-2.09043 -0.56972,-17.403258 l -0.0102,-15.742712 2.44739,-0.07608 2.4474,-0.07607 v 7.060514 7.060515 l 0.60161,-0.892728 c 1.56474,-2.321923 5.21496,-3.2175 9.04643,-2.219529 3.20198,0.834009 5.19421,2.399257 6.39662,5.02566 0.87053,1.901499 1.1483,3.650869 1.28015,8.062509 0.27496,9.199609 -2.10057,13.619369 -8.19669,15.250219 -1.75078,0.46837 -5.75011,0.42623 -7.39288,-0.0779 z m 6.49531,-4.951 c 1.40139,-0.66989 2.60987,-1.95865 3.38206,-3.60674 0.64143,-1.36901 0.68161,-1.69814 0.67829,-5.556249 -0.003,-3.1889 -0.10522,-4.39714 -0.46075,-5.43234 -1.99463,-5.807759 -9.23139,-5.807759 -11.22454,0 -0.5714,1.66496 -0.66746,9.662769 -0.13719,11.422529 0.38795,1.28749 1.62048,2.60265 3.10346,3.31151 1.26411,0.60425 3.22666,0.54581 4.65867,-0.13871 z m 36.45034,5.14101 c -2.86373,-0.55868 -5.63198,-2.86262 -6.48265,-5.39534 -0.73178,-2.17877 -0.62908,-5.24545 0.23901,-7.13684 1.7518,-3.816799 4.64695,-5.101829 11.69101,-5.189139 2.03163,-0.0252 3.74448,-0.0964 3.80632,-0.15823 0.2984,-0.2984 -0.60554,-3.52815 -1.21109,-4.32724 -1.09959,-1.451009 -2.65508,-1.980129 -5.34563,-1.818389 -1.24652,0.0749 -3.25381,0.3917 -4.46064,0.70393 -1.20684,0.31223 -2.30355,0.50013 -2.43715,0.41756 -0.1336,-0.0826 -0.32573,-0.59243 -0.42697,-1.13303 -0.10123,-0.540596 -0.34775,-1.375097 -0.54783,-1.854445 -0.20007,-0.479348 -0.29566,-0.98175 -0.21241,-1.116446 0.21936,-0.354926 3.38152,-1.248228 5.68631,-1.606368 2.64307,-0.410704 6.3822,-0.15327 8.3655,0.575956 2.33627,0.859007 4.12344,2.790721 4.91623,5.313863 0.57018,1.814649 0.60889,2.375279 0.60882,8.817039 -5e-5,5.765429 -0.0739,7.117489 -0.45609,8.351249 -0.88316,2.85093 -2.77719,4.61583 -5.82399,5.42691 -1.45391,0.38704 -6.18998,0.46426 -7.90875,0.12896 z m 6.92994,-5.20516 c 1.77043,-1.03673 2.34719,-2.50803 2.35786,-6.01479 l 0.006,-2.07533 -3.77031,0.097 c -3.29921,0.0848 -3.91253,0.17213 -4.90845,0.69861 -1.27367,0.67331 -2.69893,2.64825 -2.69733,3.73762 0.001,0.77735 0.83464,2.2531 1.67343,2.96288 1.53862,1.30197 5.57201,1.62847 7.33849,0.59406 z m -76.09176,4.86829 c -0.0718,-0.1876 -0.0996,-6.59187 -0.0618,-14.231709 l 0.0688,-13.890624 h 2.38125 2.38125 v 14.155204 14.155209 l -2.31943,0.0765 c -1.70628,0.0563 -2.35396,-0.0137 -2.45004,-0.26459 z m 10.10425,-0.0596 c -0.0802,-0.20897 -0.12269,-6.60857 -0.0945,-14.221349 l 0.0513,-13.841424 h 2.38125 2.38125 l 0.0684,14.221354 0.0684,14.221349 h -2.35524 c -1.80279,0 -2.38944,-0.0891 -2.50104,-0.37993 z m 35.11607,-0.30847 c -0.095,-0.37862 -0.11053,-4.99229 -0.0344,-10.25261 0.12618,-8.723899 0.18658,-9.715399 0.68747,-11.285189 1.29659,-4.063497 4.3606,-6.218859 9.29103,-6.535729 2.06626,-0.132795 5.29988,0.255791 5.29988,0.636886 0,0.08132 -0.2566,1.08418 -0.57023,2.228585 l -0.57023,2.080739 -2.15656,-0.16577 c -2.49177,-0.19153 -3.87041,0.25949 -5.3705,1.756959 -0.75709,0.75576 -1.76191,3.14282 -1.62992,3.87205 0.0228,0.12569 0.0273,4.30641 0.01,9.290499 l -0.0313,9.06198 h -2.37621 c -2.33876,0 -2.37893,-0.0108 -2.54898,-0.6884 z m -66.274827,-31.525 c -0.51012,-0.545486 -1.07676,-1.190866 -1.25921,-1.434176 -1.24584,-1.661446 -2.15941,-7.647514 -1.27868,-8.378449 0.35742,-0.296635 0.50679,-0.203116 1.06376,0.665993 0.37336,0.58261 0.71063,1.59372 0.79586,2.385928 0.0814,0.75616 0.33924,1.744826 0.57309,2.197036 0.23384,0.45221 0.49499,1.440876 0.58032,2.197036 0.0853,0.756161 0.32722,1.715942 0.53753,2.132844 0.60857,1.20638 0.0232,1.341514 -1.01267,0.233788 z m 2.33518,-0.912463 c -0.78294,-1.207471 -0.949,-2.328288 -0.34497,-2.328288 0.41496,0 2.3036,2.906953 2.1013,3.234277 -0.31707,0.513043 -1.09712,0.110664 -1.75633,-0.905989 z m 19.784637,0.858874 c -0.29105,-0.120382 -0.81183,-0.523184 -1.15731,-0.895117 -0.55328,-0.595656 -0.61424,-0.868323 -0.51154,-2.288169 0.14787,-2.044287 0.70615,-2.673019 2.52899,-2.848171 1.4406,-0.138425 2.76691,0.371144 3.23306,1.242139 0.42376,0.791816 0.32892,2.99125 -0.15903,3.6879 -0.76268,1.088877 -2.67211,1.623444 -3.93417,1.101418 z m 10.26163,0.07799 c -0.32245,-0.06853 -0.90073,-0.498295 -1.28506,-0.955046 -0.61462,-0.730435 -0.68434,-1.004578 -0.57889,-2.276186 0.10157,-1.224812 0.25311,-1.564764 0.99171,-2.224699 0.78581,-0.702127 1.01127,-0.767402 2.28555,-0.661731 1.64496,0.136414 2.35778,0.662517 2.77233,2.04614 0.37045,1.236472 -0.0824,3.038228 -0.89777,3.571827 -0.65206,0.426738 -2.38899,0.690716 -3.28787,0.499695 z m 39.75546,-1.455516 c -0.29653,-0.177535 -1.20228,-0.272483 -2.11667,-0.221882 -0.87909,0.04865 -4.69397,0.09826 -8.47751,0.110241 -6.435,0.02037 -6.93758,-0.01251 -7.78397,-0.509799 -1.26347,-0.74232 -1.8095,-2.382676 -1.36737,-4.107791 0.1744,-0.680468 0.2409,-1.540798 0.14777,-1.911847 -0.21966,-0.875197 -1.27098,-1.856041 -1.86604,-1.740942 -0.5115,0.09893 -1.67879,2.164519 -1.69064,2.991662 -0.0104,0.726839 -0.82481,2.78697 -1.10174,2.78697 -0.12575,0 -0.22863,-0.9525 -0.22863,-2.116667 0,-1.76389 -0.0882,-2.20486 -0.52917,-2.645833 -0.29104,-0.291042 -0.7205,-0.529167 -0.95435,-0.529167 -0.30134,0 -0.36952,-0.134858 -0.23409,-0.463021 0.1051,-0.254661 0.28802,-0.868269 0.40649,-1.363577 0.15095,-0.631092 0.8118,-1.46236 2.20859,-2.778125 1.43622,-1.352905 2.18899,-2.308765 2.69387,-3.420658 0.74796,-1.647211 1.82649,-2.555189 3.03725,-2.556962 0.3715,-5.29e-4 1.97885,0.490347 3.57187,1.090869 3.83806,1.446829 4.56223,1.507868 7.1508,0.602739 3.01212,-1.053232 4.23878,-0.986837 6.91838,0.374465 1.19732,0.608267 2.36214,1.105937 2.58849,1.105937 0.22635,0 0.97971,-0.287911 1.67413,-0.639802 1.74679,-0.885163 3.01721,-1.087554 3.93068,-0.626192 0.9818,0.495877 2.03292,1.101442 2.19747,1.265994 0.15255,0.152551 2.72448,1.837262 3.70417,2.426375 0.3638,0.218763 1.43537,0.542631 2.38125,0.719709 2.99201,0.560125 3.39696,0.760338 3.98279,1.969132 0.35752,0.737711 0.51671,1.548122 0.48115,2.449343 -0.0926,2.345902 2.17289,5.515176 2.79099,3.904435 0.0923,-0.240493 0.0322,-0.848045 -0.13348,-1.350118 -0.34463,-1.044218 -0.0719,-1.472047 1.11487,-1.748684 0.95905,-0.223563 1.99025,0.640426 2.77445,2.324568 0.80436,1.727398 0.79157,1.946883 -0.16307,2.799185 -1.41862,1.26654 -5.07464,1.602407 -21.82791,2.005256 -3.35735,0.08073 -4.91543,0.02297 -5.28082,-0.195813 z m 5.68028,-4.32481 c 1.18291,-0.746736 2.5284,-2.584754 2.23713,-3.056043 -0.18437,-0.298304 -1.0193,-0.247404 -2.83503,0.172836 -0.59591,0.137922 -0.7276,0.07816 -0.7276,-0.330192 0,-0.274224 0.28708,-0.921067 0.63796,-1.437425 0.83064,-1.222407 1.15012,-3.186075 0.59351,-3.648019 -0.44514,-0.369435 -0.4109,-0.415042 -1.86137,2.47964 -0.4375,0.873125 -0.951,1.744832 -1.14111,1.937128 -0.49165,0.497301 -0.42942,1.224616 0.21434,2.505184 0.62047,1.234238 1.25359,2.039979 1.60294,2.039979 0.12586,0 0.70151,-0.298389 1.27923,-0.663088 z m 10.83558,-0.900856 c 0.47081,-1.136634 0.25503,-1.478764 -0.93265,-1.478764 -1.28599,0 -1.48663,-0.589621 -0.52713,-1.549122 0.69901,-0.699008 0.88547,-1.597345 0.39453,-1.900764 -0.7978,-0.493069 -2.04158,2.305775 -1.70522,3.83721 0.0986,0.448789 0.47652,1.169297 0.83988,1.601128 0.77873,0.925462 1.40457,0.760236 1.93059,-0.509688 z M 155.5634,74.14944 c 0.50932,-0.1802 1.46083,-0.36475 2.11447,-0.41011 1.26066,-0.08749 3.35317,-0.704985 3.9048,-1.1523 0.47723,-0.386982 0.40641,-1.377841 -0.13229,-1.850882 -0.72957,-0.640646 -1.61816,-0.828932 -4.0435,-0.856782 -1.93665,-0.02225 -2.46318,-0.133008 -3.70416,-0.779272 -0.79564,-0.41434 -1.88046,-1.095203 -2.41073,-1.513027 -1.80091,-1.41904 -1.90833,-1.058617 -0.7384,2.477529 0.9759,2.94967 1.26258,3.424655 2.40734,3.988726 1.04811,0.516448 1.37981,0.528698 2.60247,0.09612 z m -48.71741,4.518051 c -0.75584,-0.625237 -1.23923,-1.693741 -1.41492,-3.127595 -0.14698,-1.199446 -0.0682,-1.678781 0.4775,-2.905003 1.67233,-3.757871 4.19907,-6.216615 6.89,-6.704573 0.38534,-0.06988 0.94096,-0.20333 1.23472,-0.296563 0.8268,-0.262419 2.84262,0.848153 3.54476,1.95291 1.04663,1.646785 2.41915,2.48593 3.62924,2.21887 0.57393,-0.126664 1.59669,-2.515833 1.75413,-4.097681 0.10368,-1.041612 -0.0254,-1.552559 -0.76848,-3.042708 l -0.89308,-1.790886 0.0841,-4.630208 c 0.0791,-4.356544 0.12399,-4.708397 0.75971,-5.953124 1.38713,-2.71598 2.27736,-3.644731 4.64358,-4.844518 2.07098,-1.050084 2.38275,-1.130576 4.58507,-1.183796 1.68752,-0.04078 2.86165,0.08791 4.10738,0.450175 1.84993,0.537972 2.92387,0.440743 2.37931,-0.215411 -0.17951,-0.216298 -1.12844,-0.434549 -2.3449,-0.539321 -3.51928,-0.30311 -4.34481,-1.003481 -2.47202,-2.097247 1.16316,-0.679324 3.50373,-0.620097 4.69949,0.118919 1.13086,0.698912 1.48162,1.769436 1.12483,3.433052 -0.33237,1.549826 -1.02426,2.121199 -3.13572,2.589562 -3.05232,0.677061 -7.3992,3.86716 -7.94452,5.830339 -0.14608,0.525889 -0.26767,1.432412 -0.27021,2.014495 -0.003,0.582084 -0.3046,2.248959 -0.67128,3.704167 -0.36667,1.455208 -0.73302,3.419739 -0.81411,4.365625 -0.22832,2.663033 -1.21812,5.429419 -3.31335,9.260416 -0.74008,1.353177 -0.71792,1.34838 -1.93549,0.418983 -1.45883,-1.113549 -3.33052,-1.596128 -4.85879,-1.252749 -1.07379,0.241266 -1.13531,0.220337 -1.51593,-0.515715 -0.46045,-0.890413 -1.38975,-1.026083 -2.04778,-0.298961 -0.45402,0.501682 -1.74517,3.401002 -2.45636,5.515801 -0.57096,1.69781 -0.80836,1.953474 -1.81395,1.953474 -0.46369,0 -1.02299,-0.148828 -1.24288,-0.330729 z M 98.677983,78.0112 c -1.64347,-0.791178 -1.71155,-1.369084 -0.39143,-3.322897 l 0.93148,-1.378624 -0.16733,-2.513542 c -0.16726,-2.512375 -0.16697,-2.51401 0.64084,-3.523012 0.444487,-0.555207 1.357347,-1.448175 2.028557,-1.984375 1.50167,-1.199612 2.83836,-3.509435 3.53031,-6.100426 0.44623,-1.670909 0.71416,-2.14621 1.86903,-3.31561 0.74148,-0.7508 1.34814,-1.445249 1.34814,-1.543219 0,-0.09797 -0.44112,-0.260884 -0.98026,-0.362027 -0.78348,-0.146981 -1.2525,-0.04136 -2.33637,0.526111 -1.64659,0.862102 -6.434197,5.505765 -7.306207,7.086542 -0.70093,1.270653 -1.40155,1.571013 -1.95645,0.838744 -0.60987,-0.80482 -0.44244,-1.492498 0.76857,-3.156788 2.8822,-3.960987 9.692297,-9.847603 12.857717,-11.114151 0.67892,-0.27165 1.26459,-0.808894 1.82554,-1.674598 1.53853,-2.374361 3.00047,-3.179549 6.28369,-3.460849 2.2984,-0.196922 2.70621,-0.326718 5.75862,-1.83285 2.69184,-1.328216 3.72518,-1.692106 5.72239,-2.01513 1.34053,-0.216816 2.65699,-0.511765 2.92545,-0.655442 0.58973,-0.315611 3.31898,-0.450679 3.5919,-0.177759 0.43429,0.434287 -0.515,0.79676 -2.597,0.99163 -1.21017,0.113269 -2.3964,0.283952 -2.63606,0.379295 -0.23967,0.09534 -1.212,1.100689 -2.16074,2.2341 -1.01357,1.210845 -2.25242,2.362992 -3.00384,2.793592 -0.70336,0.403061 -1.83688,1.271526 -2.51892,1.929918 l -1.24008,1.197078 -1.32251,-0.568928 c -2.84275,-1.222917 -4.1526,-0.612939 -8.0301,3.739483 -0.87946,0.987176 -1.52487,1.869009 -1.43425,1.959629 0.29663,0.296624 2.30602,-0.789364 2.83493,-1.532155 0.60988,-0.856496 2.60978,-1.695103 3.4753,-1.457285 0.33374,0.0917 0.95095,0.451141 1.37159,0.798764 0.68607,0.566978 0.80202,0.900305 1.1264,3.238272 0.35442,2.55451 0.33043,3.267697 -0.10995,3.267697 -0.26228,0 -0.75383,-1.332357 -1.00139,-2.714281 -0.33349,-1.861585 -1.51416,-2.765989 -2.5869,-1.981584 -0.76289,0.557842 -0.60384,2.166263 0.49759,5.031949 0.51792,1.347499 0.48262,3.30014 -0.08,4.426415 -0.40788,0.816483 -0.58782,0.929423 -1.52094,0.95463 -0.58209,0.01572 -1.45983,-0.133104 -1.95054,-0.330729 -1.71944,-0.692467 -3.48462,-0.309946 -4.72035,1.022914 -0.30704,0.331179 -1.00125,1.554644 -1.54268,2.71881 -0.54142,1.164167 -1.32765,2.616944 -1.74717,3.22839 -1.03788,1.512737 -1.66114,3.05924 -2.05775,5.105985 -0.39069,2.016209 -0.80523,3.23025 -1.19565,3.501678 -0.47925,0.333179 -1.826457,0.210013 -2.793157,-0.255365 z M 111.11171,56.257296 c 0.50839,-0.421452 0.84203,-0.848598 0.74141,-0.949211 -0.27914,-0.279141 -2.59179,0.931217 -2.59179,1.35645 0,0.604784 0.85716,0.416139 1.85038,-0.407239 z m 84.18841,20.177317 c -0.13816,-0.263506 -0.33393,-0.86451 -0.43506,-1.335569 -0.10683,-0.497625 -0.70194,-1.382482 -1.42037,-2.11193 -2.29928,-2.334538 -2.41739,-2.571001 -2.26763,-4.539729 0.21107,-2.774878 0.36379,-3.472079 0.76052,-3.472079 0.63467,0 1.40388,1.403969 2.47809,4.523023 0.57161,1.659681 1.23753,3.54285 1.47984,4.184821 0.48135,1.275268 0.46439,2.965154 -0.0314,3.127886 -0.17205,0.05647 -0.42586,-0.112919 -0.56401,-0.376423 z M 94.706333,75.158263 c -0.22702,-0.273544 -0.29095,-0.977987 -0.20182,-2.223799 0.2774,-3.877016 2.71375,-5.428858 3.0488,-1.941946 0.0993,1.033404 -0.0147,1.568677 -0.56575,2.656088 -0.37956,0.749028 -0.92057,1.485205 -1.20224,1.635948 -0.6582,0.352256 -0.68438,0.349192 -1.07899,-0.126291 z m 36.118527,-1.612561 c -0.43656,-0.352642 -1.24024,-0.777213 -1.78594,-0.943493 -0.5457,-0.166283 -0.99219,-0.390584 -0.99219,-0.498446 0,-0.107863 0.40601,-0.799434 0.90224,-1.536825 0.93743,-1.393023 1.47901,-3.261314 1.47901,-5.102153 0,-0.589584 0.34745,-2.280364 0.7721,-3.757289 0.42465,-1.476923 0.92012,-3.816408 1.10103,-5.198856 0.18483,-1.412357 0.54155,-2.874148 0.81425,-3.336655 0.62083,-1.052955 2.20045,-2.146321 3.83533,-2.654702 1.40618,-0.437267 6.26149,-0.792274 7.29343,-0.533273 0.90311,0.226668 1.41689,1.927111 0.99684,3.299206 -0.69251,2.26206 -2.18236,4.54834 -2.96391,4.54834 -0.19087,0 -1.13856,-0.714375 -2.10597,-1.5875 -0.96741,-0.873125 -1.88849,-1.5875 -2.04685,-1.5875 -0.56821,0 -0.27265,0.768589 0.50983,1.325759 1.31448,0.93599 1.64904,1.905288 1.6843,4.879657 0.0412,3.472669 -0.18968,3.887793 -3.19943,5.753764 -2.54476,1.577689 -3.07918,2.289058 -3.05237,4.062997 0.0158,1.042474 -0.029,1.105736 -0.94376,1.333505 -0.80241,0.199792 -1.00503,0.39824 -1.23225,1.20687 l -0.27194,0.967759 z m 66.46138,-0.989121 c -0.5873,-0.896326 -1.24159,-2.538227 -1.24159,-3.11567 0,-1.01968 0.506,-0.437943 1.23364,1.418297 0.41291,1.053332 0.79042,2.005832 0.83893,2.116666 0.21156,0.483397 -0.4623,0.143378 -0.83098,-0.419293 z m -10.62496,-4.387858 c -0.74348,-1.467175 -1.52951,-2.134615 -2.51561,-2.136073 -0.79888,-0.0011 -2.34116,-0.890641 -2.56762,-1.480802 -0.1076,-0.280408 0.27483,-0.856596 1.1483,-1.730062 l 1.30963,-1.309632 0.94246,0.739388 c 0.51835,0.406665 1.60228,1.156108 2.40874,1.665431 2.13443,1.348009 2.61454,2.849054 1.49214,4.665125 -0.78952,1.277474 -1.42085,1.159814 -2.21804,-0.413375 z M 98.289873,64.644577 c 0.36497,-2.188118 2.784567,-5.754687 3.904027,-5.754687 0.6448,0 1.24659,1.058637 1.24659,2.192932 0,1.805676 -0.95241,3.033776 -3.12364,4.027792 -2.058507,0.942417 -2.254397,0.897377 -2.026977,-0.466037 z m 78.572487,-1.361808 c -0.43656,-0.1919 -1.40796,-0.83626 -2.15866,-1.431914 -1.26545,-1.004086 -1.39427,-1.214099 -1.76775,-2.881884 -0.80814,-3.608758 -0.81237,-3.706524 -0.17516,-4.047548 0.94421,-0.505328 1.6849,-0.128453 2.32108,1.181013 0.32065,0.659973 0.67505,1.199954 0.78757,1.199954 0.11252,0 0.50102,0.274021 0.86333,0.608936 1.24587,1.151649 2.37855,1.098232 2.37855,-0.112165 0,-0.734095 0.76639,-0.603171 1.06488,0.181911 0.35599,0.936332 0.32361,3.121237 -0.0587,3.960299 -0.24556,0.538949 -1.95577,1.75859 -2.38471,1.700663 -0.0422,-0.0057 -0.43388,-0.167365 -0.87044,-0.359265 z m -23.28333,-1.326793 c -0.43657,-0.194032 -1.08454,-0.667903 -1.43995,-1.053049 -0.35541,-0.385144 -1.20594,-1.059447 -1.89007,-1.498452 -1.82383,-1.170353 -2.01802,-1.846149 -1.34551,-4.682577 0.8795,-3.709448 1.00084,-5.662962 0.41352,-6.657218 -0.45099,-0.763458 -0.60348,-0.831858 -1.81929,-0.816044 -2.07484,0.02698 -3.86372,-0.28203 -4.02211,-0.694785 -0.21217,-0.552892 0.42363,-1.895366 1.17907,-2.489595 1.04836,-0.824644 3.44682,-0.552608 6.08657,0.690346 1.18199,0.556554 2.22833,2.214846 2.37051,3.756896 0.10896,1.181819 0.0561,1.305808 -0.96551,2.264767 -1.11944,1.050798 -1.26421,1.455655 -0.81839,2.28868 0.78046,1.458306 3.70434,0.582895 6.48449,-1.94146 2.17936,-1.978842 2.85522,-2.274866 3.61792,-1.584629 0.56315,0.509646 1.30639,1.77982 1.2482,2.133148 -0.18524,1.124831 0.0825,1.711129 0.94621,2.072013 1.34762,0.56307 2.94048,0.494101 3.59003,-0.155443 0.29635,-0.296352 0.49466,-0.623774 0.4407,-0.727604 -0.054,-0.103833 -0.34944,-0.739167 -0.65662,-1.411851 -0.56018,-1.226733 -1.82619,-2.423994 -3.43175,-3.245398 -1.13077,-0.578495 -1.23169,-1.584637 -0.1323,-1.318829 1.6267,0.393293 3.46133,2.419038 5.19579,5.737048 1.56504,2.993911 2.02713,4.707498 1.58214,5.867138 -0.19206,0.500501 -3.46411,3.046645 -3.91525,3.046645 -0.16475,0 -1.00647,-0.714375 -1.87049,-1.5875 l -1.57093,-1.5875 -1.72808,0.0011 c -2.02318,0.0013 -3.21164,0.64234 -5.03823,2.717451 -1.19436,1.356873 -1.32646,1.402998 -2.51067,0.87667 z m 36.01631,-0.557363 c -0.23642,-0.236421 -0.42986,-0.581491 -0.42986,-0.766823 0,-0.387451 0.83901,-1.212734 1.2329,-1.212734 0.38808,0 0.95133,1.172803 0.84102,1.751161 -0.12301,0.644908 -1.09282,0.779637 -1.64406,0.228396 z m -6.92417,-1.83524 c -0.35719,-0.357187 -0.64944,-0.797271 -0.64944,-0.977963 0,-0.180695 -0.30102,-0.76291 -0.66893,-1.293813 -0.7211,-1.040545 -0.66351,-1.71863 0.16838,-1.982663 0.6233,-0.197829 4.86662,2.046462 5.26038,2.78221 0.75395,1.408774 0.0455,2.121662 -2.10862,2.121662 -1.08304,0 -1.48167,-0.129326 -2.00177,-0.649433 z m -9.8238,-7.622696 c -1.09021,-0.619228 -3.74463,-4.163287 -3.11822,-4.163287 1.08493,0 4.81042,3.354615 4.53181,4.080663 -0.19158,0.499237 -0.63453,0.525127 -1.41359,0.08262 z M 154.17434,44.22376 c -0.83675,-0.935689 -1.52136,-1.839057 -1.52136,-2.007484 0,-0.444583 1.94781,-0.08303 3.63802,0.675296 0.95763,0.429645 1.38907,0.776737 1.38907,1.117513 0,0.503175 -1.27908,1.916344 -1.73427,1.916074 -0.13756,-8e-5 -0.93472,-0.76571 -1.77146,-1.701399 z M 142.05395,42.5991 c -0.41029,-0.494368 -0.0619,-1.25153 0.63937,-1.389547 0.3082,-0.06066 0.43466,0.103124 0.43466,0.562915 0,0.907598 -0.61439,1.380466 -1.07403,0.826632 z"
+       id="path965" />
+  </g>
+</svg>
diff --git a/src/res/favicons/favicon-128-light.png b/src/res/favicons/favicon-128-light.png
new file mode 100644
index 0000000000000000000000000000000000000000..2e7496a4689c3a850f19de1b8722ce816af66b49
Binary files /dev/null and b/src/res/favicons/favicon-128-light.png differ
diff --git a/src/res/favicons/favicon-128-light.svg b/src/res/favicons/favicon-128-light.svg
new file mode 100644
index 0000000000000000000000000000000000000000..8e5472b32795cab964d9e78f993908d64edaae47
--- /dev/null
+++ b/src/res/favicons/favicon-128-light.svg
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="128mm"
+   height="128mm"
+   viewBox="0 0 128 128"
+   version="1.1"
+   id="svg8"
+   inkscape:version="1.0.2 (394de47547, 2021-03-26)"
+   sodipodi:docname="favicon-128-light.svg">
+  <defs
+     id="defs2" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1.4"
+     inkscape:cx="324.70471"
+     inkscape:cy="279.91944"
+     inkscape:document-units="mm"
+     inkscape:current-layer="layer2"
+     inkscape:document-rotation="0"
+     showgrid="false"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0"
+     inkscape:window-width="1853"
+     inkscape:window-height="1025"
+     inkscape:window-x="67"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1" />
+  <metadata
+     id="metadata5">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer2"
+     inkscape:label="main"
+     style="display:inline"
+     transform="translate(-79.414325,-20.601907)">
+    <path
+       style="fill:#000000;stroke-width:0.264583"
+       d="m 162.83944,128.89555 c -1.71263,-0.76119 -2.51354,-2.36516 -2.51354,-5.03388 0,-2.25335 0.78988,-3.77883 2.38125,-4.59886 2.34364,-1.20767 5.04289,-0.21921 5.96015,2.18259 0.5625,1.4729 0.4413,4.34887 -0.24282,5.76143 -0.83944,1.73329 -3.604,2.5692 -5.58504,1.68872 z m 3.49261,-1.63593 c 1.05602,-0.66537 1.47806,-3.54272 0.76867,-5.24053 -1.06024,-2.53752 -4.2164,-2.24958 -4.94489,0.45112 -0.36533,1.3544 -0.16041,3.36382 0.43584,4.27382 0.74822,1.14193 2.38765,1.36792 3.74038,0.51559 z m -39.87282,-3.28224 v -5.02708 h 3.3073 3.30729 v 0.66146 c 0,0.65913 -0.009,0.66145 -2.51354,0.66145 h -2.51355 v 1.45521 1.45521 h 2.24896 2.24896 v 0.79375 0.79375 h -2.24896 -2.24896 v 1.45521 1.45521 h 2.4915 c 2.49379,0 2.90743,0.15781 2.6006,0.99219 -0.0884,0.24024 -1.01868,0.33072 -3.40061,0.33072 h -3.27899 z m 7.9375,4.81268 c 0,-0.13636 0.65485,-1.20951 1.45521,-2.38478 0.80037,-1.17527 1.45521,-2.3002 1.45521,-2.49985 0,-0.19964 -0.60994,-1.24739 -1.35542,-2.32833 -1.81084,-2.62569 -1.8116,-2.62907 -0.59112,-2.61522 0.96613,0.011 1.07801,0.10373 2.1,1.74113 l 1.0795,1.72954 1.13439,-1.74113 c 1.02835,-1.57837 1.21805,-1.74112 2.02935,-1.74112 0.49223,0 0.89497,0.0976 0.89497,0.2168 0,0.11923 -0.45048,0.82058 -1.00107,1.55853 -0.55059,0.73796 -1.22383,1.75537 -1.49608,2.26091 l -0.495,0.91916 1.62837,2.43443 c 0.8956,1.33894 1.62836,2.48616 1.62836,2.54938 0,0.0632 -0.44834,0.11495 -0.99631,0.11495 -0.9347,0 -1.06576,-0.10637 -2.11905,-1.71979 -0.61751,-0.94588 -1.21097,-1.71645 -1.31879,-1.71237 -0.10783,0.004 -0.67231,0.74822 -1.25439,1.65365 -0.89834,1.39735 -1.18833,1.65876 -1.91823,1.72913 -0.47294,0.0456 -0.8599,-0.0286 -0.8599,-0.16502 z m 9.58531,-4.74653 0.072,-4.96094 2.36594,-0.0772 c 3.55309,-0.11592 4.9101,0.71487 4.9101,3.00606 0,2.39475 -0.96061,3.25698 -3.89065,3.49217 l -1.6656,0.1337 v 1.68357 1.68356 h -0.93188 -0.93188 z m 5.23136,-0.59532 c 0.52634,-0.52634 0.69769,-1.71985 0.32668,-2.27549 -0.36931,-0.55309 -1.35454,-0.89951 -2.55824,-0.89951 h -1.20802 v 1.85209 1.85208 h 1.45521 c 1.10243,0 1.58349,-0.12828 1.98437,-0.52917 z m 3.70417,0.51241 v -5.04385 l 0.85989,0.0829 0.8599,0.0829 0.0727,4.29948 0.0727,4.29948 h 2.2424 c 2.21551,0 2.24241,0.008 2.24241,0.66146 v 0.66145 h -3.175 -3.175 z m 17.99166,0.0168 v -5.02708 l 2.71198,0.001 c 2.22127,7.9e-4 2.86498,0.0943 3.55752,0.51654 1.45573,0.88762 1.51248,3.41795 0.10255,4.57238 l -0.73112,0.59863 0.8837,2.07396 c 0.48604,1.14069 0.88371,2.1305 0.88371,2.19958 0,0.0691 -0.38396,0.0883 -0.85324,0.0427 -0.77802,-0.0756 -0.91584,-0.23454 -1.56315,-1.8027 -0.91281,-2.21133 -1.12685,-2.42774 -2.30398,-2.32964 l -0.96817,0.0806 -0.0773,2.05052 -0.0773,2.05052 h -0.78261 -0.78263 v -5.02708 z m 5.1237,-1.07753 c 0.83208,-0.65453 0.88983,-1.37562 0.16797,-2.09748 -0.41157,-0.41157 -0.88194,-0.52917 -2.11667,-0.52917 h -1.5875 v 1.5875 1.5875 h 1.41953 c 1.03547,0 1.60814,-0.14836 2.11667,-0.54837 z m 4.4013,1.07752 v -5.02708 h 3.3073 3.30729 v 0.66146 c 0,0.65913 -0.009,0.66145 -2.51354,0.66145 h -2.51355 v 1.45521 1.45521 h 2.24896 2.24896 v 0.79375 0.79375 h -2.24896 -2.24896 v 1.45521 1.45521 h 2.51355 c 2.50472,0 2.51354,0.002 2.51354,0.66146 v 0.66145 h -3.30729 -3.3073 z m 8.73125,0 v -5.02708 h 2.75783 c 2.5548,0 2.81563,0.0486 3.543,0.66068 0.43184,0.36337 0.86108,0.96314 0.95387,1.33283 0.25133,1.00138 -0.29507,2.66624 -1.05728,3.22147 l -0.67115,0.4889 0.931,2.17514 0.931,2.17514 h -0.87632 c -0.83195,0 -0.914,-0.0904 -1.62079,-1.78593 -0.98454,-2.36185 -1.10215,-2.48856 -2.22268,-2.39463 l -0.94868,0.0795 -0.0773,2.05052 -0.0773,2.05052 h -0.78261 -0.78263 z m 4.96821,-0.94104 c 0.70292,-0.49234 0.77514,-1.58868 0.15016,-2.27927 -0.32907,-0.36362 -0.82218,-0.48386 -1.98437,-0.48386 h -1.5465 v 1.5875 1.5875 h 1.39634 c 0.83558,0 1.63249,-0.1654 1.98437,-0.41187 z m -97.572367,-9.5378 c -1.95888,-0.34634 -5.82084,-1.69103 -5.82084,-2.02677 0,-0.26055 1.56112,-3.71342 1.77614,-3.92844 0.17279,-0.17279 0.61281,-0.12624 1.29339,0.13682 1.5231,0.58871 4.90435,1.21598 6.554677,1.21598 2.60677,0 5.19386,-1.69122 5.19148,-3.39374 -0.003,-1.9815 -1.93734,-3.38013 -6.084437,-4.39883 -4.65894,-1.144439 -6.70269,-2.519669 -8.03124,-5.404209 -0.71141,-1.5446 -0.75135,-4.927329 -0.0761,-6.446859 0.70597,-1.588706 2.48912,-3.227144 4.27091,-3.924299 1.36156,-0.532739 2.0582,-0.621779 4.894797,-0.625631 3.42639,-0.0047 5.55454,0.402283 7.71765,1.475732 l 0.97077,0.481753 -0.70619,2.010008 c -0.80831,2.300676 -0.49127,2.212587 -4.26017,1.183737 -3.97503,-1.085119 -6.995447,-0.45827 -8.048857,1.670449 -0.41938,0.84746 -0.44249,1.10637 -0.15967,1.78914 0.64008,1.54529 2.27792,2.40158 6.602567,3.4519 5.57621,1.35429 8.19428,4.460389 7.88051,9.349499 -0.21116,3.29034 -1.90364,5.60538 -4.96953,6.79752 -1.17157,0.45555 -2.12561,0.57923 -4.89479,0.63451 -1.891777,0.0378 -3.737247,0.0161 -4.101047,-0.0483 z m 44.068567,-0.278 c -2.90689,-0.89207 -5.0522,-3.00939 -6.05006,-5.97114 -0.5442,-1.61522 -0.55975,-2.09043 -0.56972,-17.403258 l -0.0102,-15.742712 2.44739,-0.07608 2.4474,-0.07607 v 7.060514 7.060515 l 0.60161,-0.892728 c 1.56474,-2.321923 5.21496,-3.2175 9.04643,-2.219529 3.20198,0.834009 5.19421,2.399257 6.39662,5.02566 0.87053,1.901499 1.1483,3.650869 1.28015,8.062509 0.27496,9.199609 -2.10057,13.619369 -8.19669,15.250219 -1.75078,0.46837 -5.75011,0.42623 -7.39288,-0.0779 z m 6.49531,-4.951 c 1.40139,-0.66989 2.60987,-1.95865 3.38206,-3.60674 0.64143,-1.36901 0.68161,-1.69814 0.67829,-5.556249 -0.003,-3.1889 -0.10522,-4.39714 -0.46075,-5.43234 -1.99463,-5.807759 -9.23139,-5.807759 -11.22454,0 -0.5714,1.66496 -0.66746,9.662769 -0.13719,11.422529 0.38795,1.28749 1.62048,2.60265 3.10346,3.31151 1.26411,0.60425 3.22666,0.54581 4.65867,-0.13871 z m 36.45034,5.14101 c -2.86373,-0.55868 -5.63198,-2.86262 -6.48265,-5.39534 -0.73178,-2.17877 -0.62908,-5.24545 0.23901,-7.13684 1.7518,-3.816799 4.64695,-5.101829 11.69101,-5.189139 2.03163,-0.0252 3.74448,-0.0964 3.80632,-0.15823 0.2984,-0.2984 -0.60554,-3.52815 -1.21109,-4.32724 -1.09959,-1.451009 -2.65508,-1.980129 -5.34563,-1.818389 -1.24652,0.0749 -3.25381,0.3917 -4.46064,0.70393 -1.20684,0.31223 -2.30355,0.50013 -2.43715,0.41756 -0.1336,-0.0826 -0.32573,-0.59243 -0.42697,-1.13303 -0.10123,-0.540596 -0.34775,-1.375097 -0.54783,-1.854445 -0.20007,-0.479348 -0.29566,-0.98175 -0.21241,-1.116446 0.21936,-0.354926 3.38152,-1.248228 5.68631,-1.606368 2.64307,-0.410704 6.3822,-0.15327 8.3655,0.575956 2.33627,0.859007 4.12344,2.790721 4.91623,5.313863 0.57018,1.814649 0.60889,2.375279 0.60882,8.817039 -5e-5,5.765429 -0.0739,7.117489 -0.45609,8.351249 -0.88316,2.85093 -2.77719,4.61583 -5.82399,5.42691 -1.45391,0.38704 -6.18998,0.46426 -7.90875,0.12896 z m 6.92994,-5.20516 c 1.77043,-1.03673 2.34719,-2.50803 2.35786,-6.01479 l 0.006,-2.07533 -3.77031,0.097 c -3.29921,0.0848 -3.91253,0.17213 -4.90845,0.69861 -1.27367,0.67331 -2.69893,2.64825 -2.69733,3.73762 0.001,0.77735 0.83464,2.2531 1.67343,2.96288 1.53862,1.30197 5.57201,1.62847 7.33849,0.59406 z m -76.09176,4.86829 c -0.0718,-0.1876 -0.0996,-6.59187 -0.0618,-14.231709 l 0.0688,-13.890624 h 2.38125 2.38125 v 14.155204 14.155209 l -2.31943,0.0765 c -1.70628,0.0563 -2.35396,-0.0137 -2.45004,-0.26459 z m 10.10425,-0.0596 c -0.0802,-0.20897 -0.12269,-6.60857 -0.0945,-14.221349 l 0.0513,-13.841424 h 2.38125 2.38125 l 0.0684,14.221354 0.0684,14.221349 h -2.35524 c -1.80279,0 -2.38944,-0.0891 -2.50104,-0.37993 z m 35.11607,-0.30847 c -0.095,-0.37862 -0.11053,-4.99229 -0.0344,-10.25261 0.12618,-8.723899 0.18658,-9.715399 0.68747,-11.285189 1.29659,-4.063497 4.3606,-6.218859 9.29103,-6.535729 2.06626,-0.132795 5.29988,0.255791 5.29988,0.636886 0,0.08132 -0.2566,1.08418 -0.57023,2.228585 l -0.57023,2.080739 -2.15656,-0.16577 c -2.49177,-0.19153 -3.87041,0.25949 -5.3705,1.756959 -0.75709,0.75576 -1.76191,3.14282 -1.62992,3.87205 0.0228,0.12569 0.0273,4.30641 0.01,9.290499 l -0.0313,9.06198 h -2.37621 c -2.33876,0 -2.37893,-0.0108 -2.54898,-0.6884 z m -66.274827,-31.525 c -0.51012,-0.545486 -1.07676,-1.190866 -1.25921,-1.434176 -1.24584,-1.661446 -2.15941,-7.647514 -1.27868,-8.378449 0.35742,-0.296635 0.50679,-0.203116 1.06376,0.665993 0.37336,0.58261 0.71063,1.59372 0.79586,2.385928 0.0814,0.75616 0.33924,1.744826 0.57309,2.197036 0.23384,0.45221 0.49499,1.440876 0.58032,2.197036 0.0853,0.756161 0.32722,1.715942 0.53753,2.132844 0.60857,1.20638 0.0232,1.341514 -1.01267,0.233788 z m 2.33518,-0.912463 c -0.78294,-1.207471 -0.949,-2.328288 -0.34497,-2.328288 0.41496,0 2.3036,2.906953 2.1013,3.234277 -0.31707,0.513043 -1.09712,0.110664 -1.75633,-0.905989 z m 19.784637,0.858874 c -0.29105,-0.120382 -0.81183,-0.523184 -1.15731,-0.895117 -0.55328,-0.595656 -0.61424,-0.868323 -0.51154,-2.288169 0.14787,-2.044287 0.70615,-2.673019 2.52899,-2.848171 1.4406,-0.138425 2.76691,0.371144 3.23306,1.242139 0.42376,0.791816 0.32892,2.99125 -0.15903,3.6879 -0.76268,1.088877 -2.67211,1.623444 -3.93417,1.101418 z m 10.26163,0.07799 c -0.32245,-0.06853 -0.90073,-0.498295 -1.28506,-0.955046 -0.61462,-0.730435 -0.68434,-1.004578 -0.57889,-2.276186 0.10157,-1.224812 0.25311,-1.564764 0.99171,-2.224699 0.78581,-0.702127 1.01127,-0.767402 2.28555,-0.661731 1.64496,0.136414 2.35778,0.662517 2.77233,2.04614 0.37045,1.236472 -0.0824,3.038228 -0.89777,3.571827 -0.65206,0.426738 -2.38899,0.690716 -3.28787,0.499695 z m 39.75546,-1.455516 c -0.29653,-0.177535 -1.20228,-0.272483 -2.11667,-0.221882 -0.87909,0.04865 -4.69397,0.09826 -8.47751,0.110241 -6.435,0.02037 -6.93758,-0.01251 -7.78397,-0.509799 -1.26347,-0.74232 -1.8095,-2.382676 -1.36737,-4.107791 0.1744,-0.680468 0.2409,-1.540798 0.14777,-1.911847 -0.21966,-0.875197 -1.27098,-1.856041 -1.86604,-1.740942 -0.5115,0.09893 -1.67879,2.164519 -1.69064,2.991662 -0.0104,0.726839 -0.82481,2.78697 -1.10174,2.78697 -0.12575,0 -0.22863,-0.9525 -0.22863,-2.116667 0,-1.76389 -0.0882,-2.20486 -0.52917,-2.645833 -0.29104,-0.291042 -0.7205,-0.529167 -0.95435,-0.529167 -0.30134,0 -0.36952,-0.134858 -0.23409,-0.463021 0.1051,-0.254661 0.28802,-0.868269 0.40649,-1.363577 0.15095,-0.631092 0.8118,-1.46236 2.20859,-2.778125 1.43622,-1.352905 2.18899,-2.308765 2.69387,-3.420658 0.74796,-1.647211 1.82649,-2.555189 3.03725,-2.556962 0.3715,-5.29e-4 1.97885,0.490347 3.57187,1.090869 3.83806,1.446829 4.56223,1.507868 7.1508,0.602739 3.01212,-1.053232 4.23878,-0.986837 6.91838,0.374465 1.19732,0.608267 2.36214,1.105937 2.58849,1.105937 0.22635,0 0.97971,-0.287911 1.67413,-0.639802 1.74679,-0.885163 3.01721,-1.087554 3.93068,-0.626192 0.9818,0.495877 2.03292,1.101442 2.19747,1.265994 0.15255,0.152551 2.72448,1.837262 3.70417,2.426375 0.3638,0.218763 1.43537,0.542631 2.38125,0.719709 2.99201,0.560125 3.39696,0.760338 3.98279,1.969132 0.35752,0.737711 0.51671,1.548122 0.48115,2.449343 -0.0926,2.345902 2.17289,5.515176 2.79099,3.904435 0.0923,-0.240493 0.0322,-0.848045 -0.13348,-1.350118 -0.34463,-1.044218 -0.0719,-1.472047 1.11487,-1.748684 0.95905,-0.223563 1.99025,0.640426 2.77445,2.324568 0.80436,1.727398 0.79157,1.946883 -0.16307,2.799185 -1.41862,1.26654 -5.07464,1.602407 -21.82791,2.005256 -3.35735,0.08073 -4.91543,0.02297 -5.28082,-0.195813 z m 5.68028,-4.32481 c 1.18291,-0.746736 2.5284,-2.584754 2.23713,-3.056043 -0.18437,-0.298304 -1.0193,-0.247404 -2.83503,0.172836 -0.59591,0.137922 -0.7276,0.07816 -0.7276,-0.330192 0,-0.274224 0.28708,-0.921067 0.63796,-1.437425 0.83064,-1.222407 1.15012,-3.186075 0.59351,-3.648019 -0.44514,-0.369435 -0.4109,-0.415042 -1.86137,2.47964 -0.4375,0.873125 -0.951,1.744832 -1.14111,1.937128 -0.49165,0.497301 -0.42942,1.224616 0.21434,2.505184 0.62047,1.234238 1.25359,2.039979 1.60294,2.039979 0.12586,0 0.70151,-0.298389 1.27923,-0.663088 z m 10.83558,-0.900856 c 0.47081,-1.136634 0.25503,-1.478764 -0.93265,-1.478764 -1.28599,0 -1.48663,-0.589621 -0.52713,-1.549122 0.69901,-0.699008 0.88547,-1.597345 0.39453,-1.900764 -0.7978,-0.493069 -2.04158,2.305775 -1.70522,3.83721 0.0986,0.448789 0.47652,1.169297 0.83988,1.601128 0.77873,0.925462 1.40457,0.760236 1.93059,-0.509688 z M 155.5634,74.14944 c 0.50932,-0.1802 1.46083,-0.36475 2.11447,-0.41011 1.26066,-0.08749 3.35317,-0.704985 3.9048,-1.1523 0.47723,-0.386982 0.40641,-1.377841 -0.13229,-1.850882 -0.72957,-0.640646 -1.61816,-0.828932 -4.0435,-0.856782 -1.93665,-0.02225 -2.46318,-0.133008 -3.70416,-0.779272 -0.79564,-0.41434 -1.88046,-1.095203 -2.41073,-1.513027 -1.80091,-1.41904 -1.90833,-1.058617 -0.7384,2.477529 0.9759,2.94967 1.26258,3.424655 2.40734,3.988726 1.04811,0.516448 1.37981,0.528698 2.60247,0.09612 z m -48.71741,4.518051 c -0.75584,-0.625237 -1.23923,-1.693741 -1.41492,-3.127595 -0.14698,-1.199446 -0.0682,-1.678781 0.4775,-2.905003 1.67233,-3.757871 4.19907,-6.216615 6.89,-6.704573 0.38534,-0.06988 0.94096,-0.20333 1.23472,-0.296563 0.8268,-0.262419 2.84262,0.848153 3.54476,1.95291 1.04663,1.646785 2.41915,2.48593 3.62924,2.21887 0.57393,-0.126664 1.59669,-2.515833 1.75413,-4.097681 0.10368,-1.041612 -0.0254,-1.552559 -0.76848,-3.042708 l -0.89308,-1.790886 0.0841,-4.630208 c 0.0791,-4.356544 0.12399,-4.708397 0.75971,-5.953124 1.38713,-2.71598 2.27736,-3.644731 4.64358,-4.844518 2.07098,-1.050084 2.38275,-1.130576 4.58507,-1.183796 1.68752,-0.04078 2.86165,0.08791 4.10738,0.450175 1.84993,0.537972 2.92387,0.440743 2.37931,-0.215411 -0.17951,-0.216298 -1.12844,-0.434549 -2.3449,-0.539321 -3.51928,-0.30311 -4.34481,-1.003481 -2.47202,-2.097247 1.16316,-0.679324 3.50373,-0.620097 4.69949,0.118919 1.13086,0.698912 1.48162,1.769436 1.12483,3.433052 -0.33237,1.549826 -1.02426,2.121199 -3.13572,2.589562 -3.05232,0.677061 -7.3992,3.86716 -7.94452,5.830339 -0.14608,0.525889 -0.26767,1.432412 -0.27021,2.014495 -0.003,0.582084 -0.3046,2.248959 -0.67128,3.704167 -0.36667,1.455208 -0.73302,3.419739 -0.81411,4.365625 -0.22832,2.663033 -1.21812,5.429419 -3.31335,9.260416 -0.74008,1.353177 -0.71792,1.34838 -1.93549,0.418983 -1.45883,-1.113549 -3.33052,-1.596128 -4.85879,-1.252749 -1.07379,0.241266 -1.13531,0.220337 -1.51593,-0.515715 -0.46045,-0.890413 -1.38975,-1.026083 -2.04778,-0.298961 -0.45402,0.501682 -1.74517,3.401002 -2.45636,5.515801 -0.57096,1.69781 -0.80836,1.953474 -1.81395,1.953474 -0.46369,0 -1.02299,-0.148828 -1.24288,-0.330729 z M 98.677983,78.0112 c -1.64347,-0.791178 -1.71155,-1.369084 -0.39143,-3.322897 l 0.93148,-1.378624 -0.16733,-2.513542 c -0.16726,-2.512375 -0.16697,-2.51401 0.64084,-3.523012 0.444487,-0.555207 1.357347,-1.448175 2.028557,-1.984375 1.50167,-1.199612 2.83836,-3.509435 3.53031,-6.100426 0.44623,-1.670909 0.71416,-2.14621 1.86903,-3.31561 0.74148,-0.7508 1.34814,-1.445249 1.34814,-1.543219 0,-0.09797 -0.44112,-0.260884 -0.98026,-0.362027 -0.78348,-0.146981 -1.2525,-0.04136 -2.33637,0.526111 -1.64659,0.862102 -6.434197,5.505765 -7.306207,7.086542 -0.70093,1.270653 -1.40155,1.571013 -1.95645,0.838744 -0.60987,-0.80482 -0.44244,-1.492498 0.76857,-3.156788 2.8822,-3.960987 9.692297,-9.847603 12.857717,-11.114151 0.67892,-0.27165 1.26459,-0.808894 1.82554,-1.674598 1.53853,-2.374361 3.00047,-3.179549 6.28369,-3.460849 2.2984,-0.196922 2.70621,-0.326718 5.75862,-1.83285 2.69184,-1.328216 3.72518,-1.692106 5.72239,-2.01513 1.34053,-0.216816 2.65699,-0.511765 2.92545,-0.655442 0.58973,-0.315611 3.31898,-0.450679 3.5919,-0.177759 0.43429,0.434287 -0.515,0.79676 -2.597,0.99163 -1.21017,0.113269 -2.3964,0.283952 -2.63606,0.379295 -0.23967,0.09534 -1.212,1.100689 -2.16074,2.2341 -1.01357,1.210845 -2.25242,2.362992 -3.00384,2.793592 -0.70336,0.403061 -1.83688,1.271526 -2.51892,1.929918 l -1.24008,1.197078 -1.32251,-0.568928 c -2.84275,-1.222917 -4.1526,-0.612939 -8.0301,3.739483 -0.87946,0.987176 -1.52487,1.869009 -1.43425,1.959629 0.29663,0.296624 2.30602,-0.789364 2.83493,-1.532155 0.60988,-0.856496 2.60978,-1.695103 3.4753,-1.457285 0.33374,0.0917 0.95095,0.451141 1.37159,0.798764 0.68607,0.566978 0.80202,0.900305 1.1264,3.238272 0.35442,2.55451 0.33043,3.267697 -0.10995,3.267697 -0.26228,0 -0.75383,-1.332357 -1.00139,-2.714281 -0.33349,-1.861585 -1.51416,-2.765989 -2.5869,-1.981584 -0.76289,0.557842 -0.60384,2.166263 0.49759,5.031949 0.51792,1.347499 0.48262,3.30014 -0.08,4.426415 -0.40788,0.816483 -0.58782,0.929423 -1.52094,0.95463 -0.58209,0.01572 -1.45983,-0.133104 -1.95054,-0.330729 -1.71944,-0.692467 -3.48462,-0.309946 -4.72035,1.022914 -0.30704,0.331179 -1.00125,1.554644 -1.54268,2.71881 -0.54142,1.164167 -1.32765,2.616944 -1.74717,3.22839 -1.03788,1.512737 -1.66114,3.05924 -2.05775,5.105985 -0.39069,2.016209 -0.80523,3.23025 -1.19565,3.501678 -0.47925,0.333179 -1.826457,0.210013 -2.793157,-0.255365 z M 111.11171,56.257296 c 0.50839,-0.421452 0.84203,-0.848598 0.74141,-0.949211 -0.27914,-0.279141 -2.59179,0.931217 -2.59179,1.35645 0,0.604784 0.85716,0.416139 1.85038,-0.407239 z m 84.18841,20.177317 c -0.13816,-0.263506 -0.33393,-0.86451 -0.43506,-1.335569 -0.10683,-0.497625 -0.70194,-1.382482 -1.42037,-2.11193 -2.29928,-2.334538 -2.41739,-2.571001 -2.26763,-4.539729 0.21107,-2.774878 0.36379,-3.472079 0.76052,-3.472079 0.63467,0 1.40388,1.403969 2.47809,4.523023 0.57161,1.659681 1.23753,3.54285 1.47984,4.184821 0.48135,1.275268 0.46439,2.965154 -0.0314,3.127886 -0.17205,0.05647 -0.42586,-0.112919 -0.56401,-0.376423 z M 94.706333,75.158263 c -0.22702,-0.273544 -0.29095,-0.977987 -0.20182,-2.223799 0.2774,-3.877016 2.71375,-5.428858 3.0488,-1.941946 0.0993,1.033404 -0.0147,1.568677 -0.56575,2.656088 -0.37956,0.749028 -0.92057,1.485205 -1.20224,1.635948 -0.6582,0.352256 -0.68438,0.349192 -1.07899,-0.126291 z m 36.118527,-1.612561 c -0.43656,-0.352642 -1.24024,-0.777213 -1.78594,-0.943493 -0.5457,-0.166283 -0.99219,-0.390584 -0.99219,-0.498446 0,-0.107863 0.40601,-0.799434 0.90224,-1.536825 0.93743,-1.393023 1.47901,-3.261314 1.47901,-5.102153 0,-0.589584 0.34745,-2.280364 0.7721,-3.757289 0.42465,-1.476923 0.92012,-3.816408 1.10103,-5.198856 0.18483,-1.412357 0.54155,-2.874148 0.81425,-3.336655 0.62083,-1.052955 2.20045,-2.146321 3.83533,-2.654702 1.40618,-0.437267 6.26149,-0.792274 7.29343,-0.533273 0.90311,0.226668 1.41689,1.927111 0.99684,3.299206 -0.69251,2.26206 -2.18236,4.54834 -2.96391,4.54834 -0.19087,0 -1.13856,-0.714375 -2.10597,-1.5875 -0.96741,-0.873125 -1.88849,-1.5875 -2.04685,-1.5875 -0.56821,0 -0.27265,0.768589 0.50983,1.325759 1.31448,0.93599 1.64904,1.905288 1.6843,4.879657 0.0412,3.472669 -0.18968,3.887793 -3.19943,5.753764 -2.54476,1.577689 -3.07918,2.289058 -3.05237,4.062997 0.0158,1.042474 -0.029,1.105736 -0.94376,1.333505 -0.80241,0.199792 -1.00503,0.39824 -1.23225,1.20687 l -0.27194,0.967759 z m 66.46138,-0.989121 c -0.5873,-0.896326 -1.24159,-2.538227 -1.24159,-3.11567 0,-1.01968 0.506,-0.437943 1.23364,1.418297 0.41291,1.053332 0.79042,2.005832 0.83893,2.116666 0.21156,0.483397 -0.4623,0.143378 -0.83098,-0.419293 z m -10.62496,-4.387858 c -0.74348,-1.467175 -1.52951,-2.134615 -2.51561,-2.136073 -0.79888,-0.0011 -2.34116,-0.890641 -2.56762,-1.480802 -0.1076,-0.280408 0.27483,-0.856596 1.1483,-1.730062 l 1.30963,-1.309632 0.94246,0.739388 c 0.51835,0.406665 1.60228,1.156108 2.40874,1.665431 2.13443,1.348009 2.61454,2.849054 1.49214,4.665125 -0.78952,1.277474 -1.42085,1.159814 -2.21804,-0.413375 z M 98.289873,64.644577 c 0.36497,-2.188118 2.784567,-5.754687 3.904027,-5.754687 0.6448,0 1.24659,1.058637 1.24659,2.192932 0,1.805676 -0.95241,3.033776 -3.12364,4.027792 -2.058507,0.942417 -2.254397,0.897377 -2.026977,-0.466037 z m 78.572487,-1.361808 c -0.43656,-0.1919 -1.40796,-0.83626 -2.15866,-1.431914 -1.26545,-1.004086 -1.39427,-1.214099 -1.76775,-2.881884 -0.80814,-3.608758 -0.81237,-3.706524 -0.17516,-4.047548 0.94421,-0.505328 1.6849,-0.128453 2.32108,1.181013 0.32065,0.659973 0.67505,1.199954 0.78757,1.199954 0.11252,0 0.50102,0.274021 0.86333,0.608936 1.24587,1.151649 2.37855,1.098232 2.37855,-0.112165 0,-0.734095 0.76639,-0.603171 1.06488,0.181911 0.35599,0.936332 0.32361,3.121237 -0.0587,3.960299 -0.24556,0.538949 -1.95577,1.75859 -2.38471,1.700663 -0.0422,-0.0057 -0.43388,-0.167365 -0.87044,-0.359265 z m -23.28333,-1.326793 c -0.43657,-0.194032 -1.08454,-0.667903 -1.43995,-1.053049 -0.35541,-0.385144 -1.20594,-1.059447 -1.89007,-1.498452 -1.82383,-1.170353 -2.01802,-1.846149 -1.34551,-4.682577 0.8795,-3.709448 1.00084,-5.662962 0.41352,-6.657218 -0.45099,-0.763458 -0.60348,-0.831858 -1.81929,-0.816044 -2.07484,0.02698 -3.86372,-0.28203 -4.02211,-0.694785 -0.21217,-0.552892 0.42363,-1.895366 1.17907,-2.489595 1.04836,-0.824644 3.44682,-0.552608 6.08657,0.690346 1.18199,0.556554 2.22833,2.214846 2.37051,3.756896 0.10896,1.181819 0.0561,1.305808 -0.96551,2.264767 -1.11944,1.050798 -1.26421,1.455655 -0.81839,2.28868 0.78046,1.458306 3.70434,0.582895 6.48449,-1.94146 2.17936,-1.978842 2.85522,-2.274866 3.61792,-1.584629 0.56315,0.509646 1.30639,1.77982 1.2482,2.133148 -0.18524,1.124831 0.0825,1.711129 0.94621,2.072013 1.34762,0.56307 2.94048,0.494101 3.59003,-0.155443 0.29635,-0.296352 0.49466,-0.623774 0.4407,-0.727604 -0.054,-0.103833 -0.34944,-0.739167 -0.65662,-1.411851 -0.56018,-1.226733 -1.82619,-2.423994 -3.43175,-3.245398 -1.13077,-0.578495 -1.23169,-1.584637 -0.1323,-1.318829 1.6267,0.393293 3.46133,2.419038 5.19579,5.737048 1.56504,2.993911 2.02713,4.707498 1.58214,5.867138 -0.19206,0.500501 -3.46411,3.046645 -3.91525,3.046645 -0.16475,0 -1.00647,-0.714375 -1.87049,-1.5875 l -1.57093,-1.5875 -1.72808,0.0011 c -2.02318,0.0013 -3.21164,0.64234 -5.03823,2.717451 -1.19436,1.356873 -1.32646,1.402998 -2.51067,0.87667 z m 36.01631,-0.557363 c -0.23642,-0.236421 -0.42986,-0.581491 -0.42986,-0.766823 0,-0.387451 0.83901,-1.212734 1.2329,-1.212734 0.38808,0 0.95133,1.172803 0.84102,1.751161 -0.12301,0.644908 -1.09282,0.779637 -1.64406,0.228396 z m -6.92417,-1.83524 c -0.35719,-0.357187 -0.64944,-0.797271 -0.64944,-0.977963 0,-0.180695 -0.30102,-0.76291 -0.66893,-1.293813 -0.7211,-1.040545 -0.66351,-1.71863 0.16838,-1.982663 0.6233,-0.197829 4.86662,2.046462 5.26038,2.78221 0.75395,1.408774 0.0455,2.121662 -2.10862,2.121662 -1.08304,0 -1.48167,-0.129326 -2.00177,-0.649433 z m -9.8238,-7.622696 c -1.09021,-0.619228 -3.74463,-4.163287 -3.11822,-4.163287 1.08493,0 4.81042,3.354615 4.53181,4.080663 -0.19158,0.499237 -0.63453,0.525127 -1.41359,0.08262 z M 154.17434,44.22376 c -0.83675,-0.935689 -1.52136,-1.839057 -1.52136,-2.007484 0,-0.444583 1.94781,-0.08303 3.63802,0.675296 0.95763,0.429645 1.38907,0.776737 1.38907,1.117513 0,0.503175 -1.27908,1.916344 -1.73427,1.916074 -0.13756,-8e-5 -0.93472,-0.76571 -1.77146,-1.701399 z M 142.05395,42.5991 c -0.41029,-0.494368 -0.0619,-1.25153 0.63937,-1.389547 0.3082,-0.06066 0.43466,0.103124 0.43466,0.562915 0,0.907598 -0.61439,1.380466 -1.07403,0.826632 z"
+       id="path965" />
+  </g>
+</svg>
diff --git a/deploy/assets/images/atlas-selection/allen-mouse-2015.png b/src/res/images/atlas-selection/allen-mouse-2015.png
similarity index 100%
rename from deploy/assets/images/atlas-selection/allen-mouse-2015.png
rename to src/res/images/atlas-selection/allen-mouse-2015.png
diff --git a/deploy/assets/images/atlas-selection/allen-mouse-2017.png b/src/res/images/atlas-selection/allen-mouse-2017.png
similarity index 100%
rename from deploy/assets/images/atlas-selection/allen-mouse-2017.png
rename to src/res/images/atlas-selection/allen-mouse-2017.png
diff --git a/deploy/assets/images/atlas-selection/allen-mouse.png b/src/res/images/atlas-selection/allen-mouse.png
similarity index 100%
rename from deploy/assets/images/atlas-selection/allen-mouse.png
rename to src/res/images/atlas-selection/allen-mouse.png
diff --git a/deploy/assets/images/atlas-selection/bugbrain.png b/src/res/images/atlas-selection/bigbrain.png
similarity index 100%
rename from deploy/assets/images/atlas-selection/bugbrain.png
rename to src/res/images/atlas-selection/bigbrain.png
diff --git a/deploy/assets/images/atlas-selection/colin27.png b/src/res/images/atlas-selection/colin27.png
similarity index 100%
rename from deploy/assets/images/atlas-selection/colin27.png
rename to src/res/images/atlas-selection/colin27.png
diff --git a/deploy/assets/images/atlas-selection/cortical-layers.png b/src/res/images/atlas-selection/cortical-layers.png
similarity index 100%
rename from deploy/assets/images/atlas-selection/cortical-layers.png
rename to src/res/images/atlas-selection/cortical-layers.png
diff --git a/deploy/assets/images/atlas-selection/cytoarchitectonic-maps.png b/src/res/images/atlas-selection/cytoarchitectonic-maps.png
similarity index 100%
rename from deploy/assets/images/atlas-selection/cytoarchitectonic-maps.png
rename to src/res/images/atlas-selection/cytoarchitectonic-maps.png
diff --git a/deploy/assets/images/atlas-selection/difumo-1024.png b/src/res/images/atlas-selection/difumo-1024.png
similarity index 100%
rename from deploy/assets/images/atlas-selection/difumo-1024.png
rename to src/res/images/atlas-selection/difumo-1024.png
diff --git a/deploy/assets/images/atlas-selection/difumo-128.png b/src/res/images/atlas-selection/difumo-128.png
similarity index 100%
rename from deploy/assets/images/atlas-selection/difumo-128.png
rename to src/res/images/atlas-selection/difumo-128.png
diff --git a/deploy/assets/images/atlas-selection/difumo-256.png b/src/res/images/atlas-selection/difumo-256.png
similarity index 100%
rename from deploy/assets/images/atlas-selection/difumo-256.png
rename to src/res/images/atlas-selection/difumo-256.png
diff --git a/deploy/assets/images/atlas-selection/difumo-512.png b/src/res/images/atlas-selection/difumo-512.png
similarity index 100%
rename from deploy/assets/images/atlas-selection/difumo-512.png
rename to src/res/images/atlas-selection/difumo-512.png
diff --git a/deploy/assets/images/atlas-selection/difumo-64.png b/src/res/images/atlas-selection/difumo-64.png
similarity index 100%
rename from deploy/assets/images/atlas-selection/difumo-64.png
rename to src/res/images/atlas-selection/difumo-64.png
diff --git a/deploy/assets/images/atlas-selection/firbe-long.png b/src/res/images/atlas-selection/firbe-long.png
similarity index 100%
rename from deploy/assets/images/atlas-selection/firbe-long.png
rename to src/res/images/atlas-selection/firbe-long.png
diff --git a/deploy/assets/images/atlas-selection/firbe-short.png b/src/res/images/atlas-selection/firbe-short.png
similarity index 100%
rename from deploy/assets/images/atlas-selection/firbe-short.png
rename to src/res/images/atlas-selection/firbe-short.png
diff --git a/src/res/images/atlas-selection/freesurfer.png b/src/res/images/atlas-selection/freesurfer.png
new file mode 100644
index 0000000000000000000000000000000000000000..c4b2819bc44292bb5fde7a86241846b33a5c50b2
Binary files /dev/null and b/src/res/images/atlas-selection/freesurfer.png differ
diff --git a/deploy/assets/images/atlas-selection/grey-white-matter.png b/src/res/images/atlas-selection/grey-white-matter.png
similarity index 100%
rename from deploy/assets/images/atlas-selection/grey-white-matter.png
rename to src/res/images/atlas-selection/grey-white-matter.png
diff --git a/deploy/assets/images/atlas-selection/icbm2009c.png b/src/res/images/atlas-selection/icbm2009c.png
similarity index 100%
rename from deploy/assets/images/atlas-selection/icbm2009c.png
rename to src/res/images/atlas-selection/icbm2009c.png
diff --git a/deploy/assets/images/atlas-selection/short-bundle-hcp.png b/src/res/images/atlas-selection/short-bundle-hcp.png
similarity index 100%
rename from deploy/assets/images/atlas-selection/short-bundle-hcp.png
rename to src/res/images/atlas-selection/short-bundle-hcp.png
diff --git a/deploy/assets/images/atlas-selection/waxholm-v1.png b/src/res/images/atlas-selection/waxholm-v1.png
similarity index 100%
rename from deploy/assets/images/atlas-selection/waxholm-v1.png
rename to src/res/images/atlas-selection/waxholm-v1.png
diff --git a/deploy/assets/images/atlas-selection/waxholm-v2.png b/src/res/images/atlas-selection/waxholm-v2.png
similarity index 100%
rename from deploy/assets/images/atlas-selection/waxholm-v2.png
rename to src/res/images/atlas-selection/waxholm-v2.png
diff --git a/deploy/assets/images/atlas-selection/waxholm-v3.png b/src/res/images/atlas-selection/waxholm-v3.png
similarity index 100%
rename from deploy/assets/images/atlas-selection/waxholm-v3.png
rename to src/res/images/atlas-selection/waxholm-v3.png
diff --git a/deploy/assets/images/atlas-selection/waxholm.png b/src/res/images/atlas-selection/waxholm.png
similarity index 100%
rename from deploy/assets/images/atlas-selection/waxholm.png
rename to src/res/images/atlas-selection/waxholm.png
diff --git a/src/routerModule/cipher.spec.ts b/src/routerModule/cipher.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..11ac7df36a335d9165ce7d77c037e0c42b94987c
--- /dev/null
+++ b/src/routerModule/cipher.spec.ts
@@ -0,0 +1,137 @@
+import { decodeToNumber, encodeNumber, encodeURIFull } from "./cipher"
+
+describe('common.ts', () => {
+  
+  const FLOAT_PRECISION = 6
+  describe('> encodeNumber/decodeToNumber', () => {
+  
+    const getCompareOriginal = (original: number[]) => (element: string, index: number) =>
+      original[index].toString().length >= element.length
+  
+    const lengthShortened = (original: number[], encodedString: string[]) =>
+      encodedString.every(getCompareOriginal(original))
+  
+    it('should encode/decode positive integer as expected', () => {
+  
+      const positiveInt = [
+        0,
+        1,
+        99999999999,
+        12347,
+      ]
+  
+      const encodedString = positiveInt.map(n => encodeNumber(n))
+      const decodedString = encodedString.map(s => decodeToNumber(s))
+      expect(decodedString).toEqual(positiveInt)
+  
+      expect(lengthShortened(positiveInt, encodedString)).toBe(true)
+    })
+  
+    it('should encode/decode ANY positive integer as expected', () => {
+      const posInt = Array(1000).fill(null).map(() => {
+        const numDig = Math.ceil(Math.random() * 7)
+        return Math.floor(Math.random() * Math.pow(10, numDig))
+      })
+      const encodedString = posInt.map(n => encodeNumber(n))
+      const decodedNumber = encodedString.map(s => decodeToNumber(s))
+      expect(decodedNumber).toEqual(posInt)
+  
+      expect(lengthShortened(posInt, encodedString)).toBe(true)
+    })
+  
+    it('should encode/decode signed integer as expected', () => {
+  
+      const signedInt = [
+        0,
+        -0,
+        -1,
+        1,
+        128,
+        -54,
+      ]
+  
+      const encodedString = signedInt.map(n => encodeNumber(n))
+      const decodedNumber = encodedString.map(s => decodeToNumber(s))
+  
+      /**
+       * -0 will be converted to 0 by the encode/decode process, but does not deep equal, according to jasmine
+       */
+      expect(decodedNumber).toEqual(signedInt.map(v => v === 0 ? 0 : v))
+  
+      expect(lengthShortened(signedInt, encodedString)).toBe(true)
+    })
+  
+    it('should encode/decode ANY signed integer as expected', () => {
+  
+      const signedInt = Array(1000).fill(null).map(() => {
+        const numDig = Math.ceil(Math.random() * 7)
+        return Math.floor(Math.random() * Math.pow(10, numDig)) * (Math.random() > 0.5 ? 1 : -1)
+      })
+      const encodedString = signedInt.map(n => encodeNumber(n))
+      const decodedNumber = encodedString.map(s => decodeToNumber(s))
+  
+      /**
+       * -0 will be converted to 0 by the encode/decode process, but does not deep equal, according to jasmine
+       */
+      expect(decodedNumber).toEqual(signedInt.map(v => v === 0 ? 0 : v))
+  
+      expect(lengthShortened(signedInt, encodedString)).toBe(true)
+    })
+  
+    it('should encode/decode float as expected', () => {
+      const floatNum = [
+        0.111,
+        12.23,
+        1723.0,
+      ]
+  
+      const encodedString = floatNum.map(f => encodeNumber(f, { float: true }))
+      const decodedNumber = encodedString.map(s => decodeToNumber(s, { float: true }))
+      expect(decodedNumber.map(n => n.toFixed(FLOAT_PRECISION))).toEqual(floatNum.map(n => n.toFixed(FLOAT_PRECISION)))
+    })
+  
+    it('should encode/decode ANY float as expected', () => {
+      const floatNums = Array(1000).fill(null).map(() => {
+        const numDig = Math.ceil(Math.random() * 7)
+        return (Math.random() > 0.5 ? 1 : -1) * Math.floor(
+          Math.random() * Math.pow(10, numDig),
+        )
+      })
+  
+      const encodedString = floatNums.map(f => encodeNumber(f, { float: true }))
+      const decodedNumber = encodedString.map(s => decodeToNumber(s, { float: true }))
+  
+      expect(floatNums.map(v => v.toFixed(FLOAT_PRECISION))).toEqual(decodedNumber.map(n => n.toFixed(FLOAT_PRECISION)))
+    })
+  
+    it('poisoned hash should throw', () => {
+      const illegialCharacters = './\\?#!@#^%&*()+={}[]\'"\n\t;:'
+      for (const char of illegialCharacters.split('')) {
+        expect(() => {
+          decodeToNumber(char)
+        }).toThrow()
+      }
+    })
+  
+    it('poisoned hash can be caught', () => {
+  
+      const testArray = ['abc', './\\', 'Cde']
+      const decodedNum = testArray.map(v => {
+        try {
+          return decodeToNumber(v)
+        } catch (e) {
+          return null
+        }
+      }).filter(v => !!v)
+      expect(decodedNum.length).toEqual(2)
+    })
+  })
+  
+  describe('> encodeURIFull', () => {
+    it('> should endcode brackets', () => {
+      expect(
+        encodeURIFull('(')
+      ).toEqual('%28')
+    })
+  })
+})
diff --git a/src/routerModule/cipher.ts b/src/routerModule/cipher.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3a8c692d7f8066bdbe79d71d2631b921a9a0f9bd
--- /dev/null
+++ b/src/routerModule/cipher.ts
@@ -0,0 +1,107 @@
+/**
+ * First attempt at encoding int (e.g. selected region, navigation location) from number (loc info density) to b64 (higher info density)
+ * The constraint is that the cipher needs to be commpatible with URI encoding
+ * and a URI compatible separator is required.
+ *
+ * The implementation below came from
+ * https://stackoverflow.com/a/6573119/6059235
+ *
+ * While a faster solution exist in the same post, this operation is expected to be done:
+ * - once per 1 sec frequency
+ * - on < 1000 numbers
+ *
+ * So performance is not really that important (Also, need to learn bitwise operation)
+ */
+
+const cipher = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-'
+export const separator = "."
+const negString = '~'
+
+const encodeInt = (number: number) => {
+  if (number % 1 !== 0) { throw new Error('cannot encodeInt on a float. Ensure float flag is set') }
+  if (isNaN(Number(number)) || number === null || number === Number.POSITIVE_INFINITY) { throw new Error('The input is not valid') }
+
+  let rixit // like 'digit', only in some non-decimal radix
+  let residual
+  let result = ''
+
+  if (number < 0) {
+    result += negString
+    residual = Math.floor(number * -1)
+  } else {
+    residual = Math.floor(number)
+  }
+
+  /* eslint-disable-next-line no-constant-condition */
+  while (true) {
+    rixit = residual % 64
+    // this.log.log("rixit : " + rixit)
+    // this.log.log("result before : " + result)
+    result = cipher.charAt(rixit) + result
+    // this.log.log("result after : " + result)
+    // this.log.log("residual before : " + residual)
+    residual = Math.floor(residual / 64)
+    // this.log.log("residual after : " + residual)
+
+    if (residual === 0) {
+      break;
+    }
+  }
+  return result
+}
+
+interface IB64EncodingOption {
+  float: boolean
+}
+
+const defaultB64EncodingOption = {
+  float: false,
+}
+
+export const encodeNumber:
+  (number: number, option?: IB64EncodingOption) => string =
+  (number: number, { float = false }: IB64EncodingOption = defaultB64EncodingOption) => {
+    if (!float) { return encodeInt(number) } else {
+      const floatArray = new Float32Array(1)
+      floatArray[0] = number
+      const intArray = new Uint32Array(floatArray.buffer)
+      const castedInt = intArray[0]
+      return encodeInt(castedInt)
+    }
+  }
+
+const decodetoInt = (encodedString: string) => {
+  let _encodedString
+  let negFlag = false
+  if (encodedString.slice(-1) === negString) {
+    negFlag = true
+    _encodedString = encodedString.slice(0, -1)
+  } else {
+    _encodedString = encodedString
+  }
+  return (negFlag ? -1 : 1) * [..._encodedString].reduce((acc, curr) => {
+    const index = cipher.indexOf(curr)
+    if (index < 0) { throw new Error(`Poisoned b64 encoding ${encodedString}`) }
+    return acc * 64 + index
+  }, 0)
+}
+
+export const decodeToNumber:
+  (encodedString: string, option?: IB64EncodingOption) => number =
+  (encodedString: string, {float = false} = defaultB64EncodingOption) => {
+    if (!float) { return decodetoInt(encodedString) } else {
+      const _int = decodetoInt(encodedString)
+      const intArray = new Uint32Array(1)
+      intArray[0] = _int
+      const castedFloat = new Float32Array(intArray.buffer)
+      return castedFloat[0]
+    }
+  }
+
+/**
+ * see https://stackoverflow.com/questions/53051415/can-you-disable-auxiliary-secondary-routes-in-angular
+ * need to encode brackets
+ */
+export const encodeURIFull = (str: string) => {
+  return encodeURI(str).replace(/[()]/g, s => `%${s.charCodeAt(0).toString(16)}`)
+}
diff --git a/src/routerModule/index.ts b/src/routerModule/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..bd2944c5560bcdfac782b8e6aa32df0846581622
--- /dev/null
+++ b/src/routerModule/index.ts
@@ -0,0 +1 @@
+export { AtlasViewerRouterModule } from './module'
diff --git a/src/routerModule/module.ts b/src/routerModule/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1053deeb997244e486942db154f13e3d1c057f51
--- /dev/null
+++ b/src/routerModule/module.ts
@@ -0,0 +1,30 @@
+import { APP_BASE_HREF } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { RouterModule } from '@angular/router'
+import { RouterService } from "./router.service";
+import { routes } from "./util";
+
+
+@NgModule({
+  imports:[
+    RouterModule.forRoot(routes, {
+      useHash: true
+    })
+  ],
+  providers: [
+    {
+      provide: APP_BASE_HREF,
+      useValue: '/'
+    },
+    RouterService
+  ],
+  exports:[
+    RouterModule
+  ]
+})
+
+export class AtlasViewerRouterModule{
+
+  // eslint-disable-next-line @typescript-eslint/no-empty-function
+  constructor(_service: RouterService){}
+}
diff --git a/src/routerModule/parseRouteToTmplParcReg.spec.ts b/src/routerModule/parseRouteToTmplParcReg.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d81c9491c540006a068f13ad7b8e573f645d0345
--- /dev/null
+++ b/src/routerModule/parseRouteToTmplParcReg.spec.ts
@@ -0,0 +1,24 @@
+import { parseSearchParamForTemplateParcellationRegion } from './parseRouteToTmplParcReg'
+
+const url = `/a:juelich:iav:atlas:v1.0.0:1/t:minds:core:referencespace:v1.0.0:dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2/p:minds:core:parcellationatlas:v1.0.0:94c1125b-b87e-45e4-901c-00daee7f2579-26/@:0.0.0.-W000..2_ZG29.-ASCS.2-8jM2._aAY3..BSR0..0.1w4W0~.0..1jtG`
+
+const fakeState = {
+}
+const fakeTmpl = {
+  '@id': 'foobar'
+}
+const fakeParc = {
+  '@id': 'buzbaz'
+}
+const fakeRegions = [{
+  ngId: 'foo',
+  labelIndex: 152
+}]
+
+describe('parseRouteToTmplParcReg.ts', () => {
+  describe('> parseSearchParamForTemplateParcellationRegion', () => {
+    it('> parses selected region properly', () => {
+      
+    })
+  })
+})
\ No newline at end of file
diff --git a/src/routerModule/parseRouteToTmplParcReg.ts b/src/routerModule/parseRouteToTmplParcReg.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d626e279fea18182e480a76e39204b633d4f6231
--- /dev/null
+++ b/src/routerModule/parseRouteToTmplParcReg.ts
@@ -0,0 +1,110 @@
+import { getGetRegionFromLabelIndexId } from 'src/util/fn'
+import { serialiseParcellationRegion } from "common/util"
+import { decodeToNumber, separator } from './cipher'
+
+import {
+  TUrlAtlas,
+  TUrlPathObj,
+} from './type'
+import { UrlTree } from '@angular/router'
+
+
+export const PARSE_ERROR = {
+  TEMPALTE_NOT_SET: 'TEMPALTE_NOT_SET',
+  TEMPLATE_NOT_FOUND: 'TEMPLATE_NOT_FOUND',
+  PARCELLATION_NOT_UPDATED: 'PARCELLATION_NOT_UPDATED',
+}
+
+export const encodeId = (id: string) => id && id.replace(/\//g, ':')
+export const decodeId = (codedId: string) => codedId && codedId.replace(/:/g, '/')
+
+export function parseSearchParamForTemplateParcellationRegion(obj: TUrlPathObj<string[], TUrlAtlas<string[]>>, fullPath: UrlTree, state: any, warnCb: Function) {
+
+  /**
+   * TODO if search param of either template or parcellation is incorrect, wrong things are searched
+   */
+  
+
+  const templateSelected = (() => {
+    const { fetchedTemplates } = state.viewerState
+
+    const searchedId = obj.t && decodeId(obj.t[0])
+
+    if (!searchedId) return null
+    const templateToLoad = fetchedTemplates.find(template => (template['@id'] || template['fullId']) === searchedId)
+    if (!templateToLoad) { throw new Error(PARSE_ERROR.TEMPLATE_NOT_FOUND) }
+    return templateToLoad
+  })()
+
+  const parcellationSelected = (() => {
+    if (!templateSelected) return null
+    const searchedId = obj.p && decodeId(obj.p[0])
+
+    const parcellationToLoad = templateSelected.parcellations.find(parcellation => (parcellation['@id'] || parcellation['fullId']) === searchedId)
+    if (!parcellationToLoad) { 
+      warnCb(`parcellation with id ${searchedId} not found... load the first parc instead`)
+    }
+    return parcellationToLoad || templateSelected.parcellations[0]
+  })()
+
+  /* selected regions */
+
+  const regionsSelected = (() => {
+    if (!parcellationSelected) return []
+
+    // TODO deprecate. Fallback (defaultNgId) (should) already exist
+    // if (!viewerState.parcellationSelected.updated) throw new Error(PARCELLATION_NOT_UPDATED)
+
+    const getRegionFromlabelIndexId = getGetRegionFromLabelIndexId({ parcellation: parcellationSelected })
+    /**
+     * either or both parcellationToLoad and .regions maybe empty
+     */
+
+    try {
+      const json = obj.r
+        ? { [obj.r[0]]: obj.r[1] }
+        : JSON.parse(fullPath.queryParams['cRegionsSelected'] || '{}')
+
+      const selectRegionIds = []
+
+      for (const ngId in json) {
+        const val = json[ngId]
+        const labelIndicies = val.split(separator).map(n => {
+          try {
+            return decodeToNumber(n)
+          } catch (e) {
+            /**
+             * TODO poisonsed encoded char, send error message
+             */
+            return null
+          }
+        }).filter(v => !!v)
+        for (const labelIndex of labelIndicies) {
+          selectRegionIds.push( serialiseParcellationRegion({ ngId, labelIndex }) )
+        }
+      }
+      return selectRegionIds
+        .map(labelIndexId => {
+          const region = getRegionFromlabelIndexId({ labelIndexId })
+          if (!region) {
+            // cb && cb({ type: ID_ERROR, message: `region with id ${labelIndexId} not found, and will be ignored.` })
+          }
+          return region
+        })
+        .filter(r => !!r)
+
+    } catch (e) {
+      /**
+       * parsing cRegionSelected error
+       */
+      // cb && cb({ type: DECODE_CIPHER_ERROR, message: `parsing cRegionSelected error ${e.toString()}` })
+    }
+    return []
+  })()
+
+  return {
+    templateSelected,
+    parcellationSelected,
+    regionsSelected,
+  }
+}
diff --git a/src/routerModule/router.service.spec.ts b/src/routerModule/router.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5507f4a5ba5d4bb05c86853bf91768b4d7eaf213
--- /dev/null
+++ b/src/routerModule/router.service.spec.ts
@@ -0,0 +1,294 @@
+import { APP_BASE_HREF, Location } from "@angular/common"
+import { discardPeriodicTasks, fakeAsync, TestBed, tick } from "@angular/core/testing"
+import { Router } from "@angular/router"
+import { RouterTestingModule } from '@angular/router/testing'
+import { MockStore, provideMockStore } from "@ngrx/store/testing"
+import { of } from "rxjs"
+import { PureContantService } from "src/util"
+import { RouterService } from "./router.service"
+import * as util from './util'
+
+const { routes, DummyCmp } = util
+const dummyPureConstantService = {
+  allFetchingReady$: of(true)
+}
+
+let cvtStateToHashedRoutesSpy: jasmine.Spy 
+let cvtFullRouteToStateSpy: jasmine.Spy 
+let location: Location
+let router: Router
+
+describe('> router.service.ts', () => {
+  describe('> RouterService', () => {
+    beforeEach(() => {
+      cvtStateToHashedRoutesSpy = jasmine.createSpy('cvtStateToHashedRoutesSpy')
+      cvtFullRouteToStateSpy = jasmine.createSpy('cvtFullRouteToState')
+
+      spyOnProperty(util, 'cvtStateToHashedRoutes').and.returnValue(cvtStateToHashedRoutesSpy)
+      spyOnProperty(util, 'cvtFullRouteToState').and.returnValue(cvtFullRouteToStateSpy)
+
+      TestBed.configureTestingModule({
+        imports: [
+          RouterTestingModule.withRoutes(routes, {
+            useHash: true
+          }),
+        ],
+        declarations: [
+          DummyCmp,
+        ],
+        providers: [
+          provideMockStore(),
+          {
+            provide: PureContantService,
+            useValue: dummyPureConstantService
+          },
+          {
+            provide: APP_BASE_HREF,
+            useValue: '/'
+          }
+        ]
+      })
+    })
+
+    afterEach(() => {
+      cvtStateToHashedRoutesSpy.calls.reset()
+      cvtFullRouteToStateSpy.calls.reset()
+    })
+    describe('> on state set', () => {
+
+      it('> should call cvtStateToHashedRoutes', fakeAsync(() => {
+        cvtStateToHashedRoutesSpy.and.callFake(() => ``)
+        const service = TestBed.inject(RouterService)
+        const store = TestBed.inject(MockStore)
+        const fakeState = {
+          foo: 'bar'
+        }
+        store.setState(fakeState)
+        tick(320)
+        expect(cvtStateToHashedRoutesSpy).toHaveBeenCalledWith(fakeState)
+      }))
+      it('> if cvtStateToHashedRoutes throws, should navigate to home', fakeAsync(() => {
+        cvtStateToHashedRoutesSpy.and.callFake(() => {
+          throw new Error(`foo bar`)
+        })
+        const service = TestBed.inject(RouterService)
+        const store = TestBed.inject(MockStore)
+        const fakeState = {
+          foo: 'bar'
+        }
+        store.setState(fakeState)
+        tick(320)
+        location = TestBed.inject(Location)
+        expect(
+          location.path()
+        ).toBe('/')
+
+      }))
+      it('> if cvtStateToHashedRoutes returns, should navigate to expected location', fakeAsync(() => {
+        cvtStateToHashedRoutesSpy.and.callFake(() => {
+          return `foo/bar`
+        })
+        const service = TestBed.inject(RouterService)
+        const store = TestBed.inject(MockStore)
+        const fakeState = {
+          foo: 'bar'
+        }
+        store.setState(fakeState)
+        tick(320)
+        location = TestBed.inject(Location)
+        expect(
+          location.path()
+        ).toBe('/foo/bar')
+      }))
+    
+      describe('> does not excessively call navigateByUrl', () => {
+        let navigateSpy: jasmine.Spy
+        let navigateByUrlSpy: jasmine.Spy
+        beforeEach(() => {
+          const router = TestBed.inject(Router)
+          navigateSpy = spyOn(router, 'navigate').and.callThrough()
+          navigateByUrlSpy = spyOn(router, 'navigateByUrl').and.callThrough()
+        })
+        afterEach(() => {
+          navigateSpy.calls.reset()
+          navigateByUrlSpy.calls.reset()
+        })
+
+        it('> navigate calls navigateByUrl', fakeAsync(() => {
+          cvtStateToHashedRoutesSpy.and.callFake(() => {
+            return `foo/bar`
+          })
+          TestBed.inject(RouterService)
+          const store = TestBed.inject(MockStore)
+          store.setState({
+            'hello': 'world'
+          })
+          tick(320)
+          expect(cvtStateToHashedRoutesSpy).toHaveBeenCalledTimes(1 + 1)
+          expect(navigateByUrlSpy).toHaveBeenCalledTimes(1)
+        }))
+
+        it('> same state should not navigate', fakeAsync(() => {
+          cvtStateToHashedRoutesSpy.and.callFake(() => {
+            return `foo/bar`
+          })
+          
+          TestBed.inject(RouterService)
+          const router = TestBed.inject(Router)
+          router.navigate(['foo', 'bar'])
+          const store = TestBed.inject(MockStore)
+          store.setState({
+            'hello': 'world'
+          })
+          tick(320)
+          expect(cvtStateToHashedRoutesSpy).toHaveBeenCalledTimes(1 + 1)
+          expect(navigateByUrlSpy).toHaveBeenCalledTimes(1)
+        }))
+
+        it('> should handle queryParam gracefully', fakeAsync(() => {
+          const searchParam = new URLSearchParams()
+          const sv = '["precomputed://https://object.cscs.ch/v1/AUTH_08c08f9f119744cbbf77e216988da3eb/imgsvc-46d9d64f-bdac-418e-a41b-b7f805068c64"]'
+          searchParam.set('standaloneVolumes', sv)
+          cvtStateToHashedRoutesSpy.and.callFake(() => {
+            return `foo/bar?${searchParam.toString()}`
+          })
+          TestBed.inject(RouterService)
+          const store = TestBed.inject(MockStore)
+
+          TestBed.inject(RouterService)
+          const router = TestBed.inject(Router)
+          router.navigate(['foo', `bar`], { queryParams: { standaloneVolumes: sv }})
+          store.setState({
+            'hello': 'world'
+          })
+          tick(320)
+          expect(cvtStateToHashedRoutesSpy).toHaveBeenCalledTimes(1 + 1)
+          expect(navigateByUrlSpy).toHaveBeenCalledTimes(1)
+        }))
+      })
+    })
+  
+    describe('> on route change', () => {
+
+      describe('> compares new state and previous state', () => {
+
+        it('> calls cvtFullRouteToState', fakeAsync(() => {
+          const fakeParsedState = {
+            bizz: 'buzz'
+          }
+          cvtFullRouteToStateSpy.and.callFake(() => fakeParsedState)
+          cvtStateToHashedRoutesSpy.and.callFake(() => {
+            return ['bizz', 'buzz']
+          })
+          router = TestBed.inject(Router)
+          router.navigate(['foo', 'bar'])
+  
+          const service = TestBed.inject(RouterService)
+  
+          tick()
+  
+          expect(cvtFullRouteToStateSpy).toHaveBeenCalledWith(
+            router.parseUrl('/foo/bar'), {}, service['logError']
+          )
+  
+          discardPeriodicTasks()
+  
+        }))
+
+        it('> calls cvtStateToHashedRoutes with current state', fakeAsync(() => {
+          const fakeParsedState = {
+            bizz: 'buzz'
+          }
+          const fakeState = {
+            foo: 'bar'
+          }
+          cvtFullRouteToStateSpy.and.callFake(() => fakeParsedState)
+          const store = TestBed.inject(MockStore)
+          store.setState(fakeState)
+
+          cvtStateToHashedRoutesSpy.and.callFake(() => {
+            return ['bizz', 'buzz']
+          })
+          router = TestBed.inject(Router)
+          router.navigate(['foo', 'bar'])
+  
+          TestBed.inject(RouterService)
+  
+          tick()
+  
+          expect(cvtStateToHashedRoutesSpy).toHaveBeenCalledWith(fakeState)
+
+          discardPeriodicTasks()
+        }))
+
+        describe('> when cvtStateToHashedRoutes ...', () => {
+          it('> ...throws, should handle elegantly', fakeAsync(() => {
+            const fakeParsedState = {
+              bizz: 'buzz'
+            }
+            cvtFullRouteToStateSpy.and.callFake(() => fakeParsedState)
+            cvtStateToHashedRoutesSpy.and.callFake(() => {
+              throw new Error(`fizz buzz`)
+            })
+            router = TestBed.inject(Router)
+            router.navigate(['foo', 'bar'])
+    
+            TestBed.inject(RouterService)
+            const store = TestBed.inject(MockStore)
+            const dispatchSpy = spyOn(store, 'dispatch')
+            
+            tick()
+
+            expect(dispatchSpy).toHaveBeenCalled()
+    
+            discardPeriodicTasks()
+          }))
+
+          it('> ... returns different value, dispatches', fakeAsync(() => {
+            const fakeParsedState = {
+              bizz: 'buzz'
+            }
+            cvtFullRouteToStateSpy.and.callFake(() => fakeParsedState)
+            cvtStateToHashedRoutesSpy.and.callFake(() => {
+              return `fizz/buzz`
+            })
+            router = TestBed.inject(Router)
+            router.navigate(['foo', 'bar'])
+    
+            TestBed.inject(RouterService)
+            const store = TestBed.inject(MockStore)
+            const dispatchSpy = spyOn(store, 'dispatch')
+            
+            tick(320)
+
+            expect(dispatchSpy).toHaveBeenCalled()
+    
+            discardPeriodicTasks()
+          }))
+
+          it('> ... returns same value, does not dispatches', fakeAsync(() => {
+            const fakeParsedState = {
+              bizz: 'buzz'
+            }
+            cvtFullRouteToStateSpy.and.callFake(() => fakeParsedState)
+            cvtStateToHashedRoutesSpy.and.callFake(() => {
+              return `foo/bar`
+            })
+            router = TestBed.inject(Router)
+            router.navigate(['foo', 'bar'])
+    
+            const service = TestBed.inject(RouterService)
+            const store = TestBed.inject(MockStore)
+            const dispatchSpy = spyOn(store, 'dispatch')
+            
+            tick(320)
+
+            expect(dispatchSpy).not.toHaveBeenCalled()
+    
+            discardPeriodicTasks()
+          }))
+        })
+      })
+    })
+  })
+})
diff --git a/src/routerModule/router.service.ts b/src/routerModule/router.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0a9ce582a948aae79538f17c67401d8c7b079257
--- /dev/null
+++ b/src/routerModule/router.service.ts
@@ -0,0 +1,104 @@
+import { Injectable } from "@angular/core";
+import { APP_BASE_HREF } from "@angular/common";
+import { Inject } from "@angular/core";
+import { NavigationEnd, Router } from '@angular/router'
+import { Store } from "@ngrx/store";
+import { debounceTime, filter, map, shareReplay, switchMapTo, take, withLatestFrom } from "rxjs/operators";
+import { generalApplyState } from "src/services/stateStore.helper";
+import { PureContantService } from "src/util";
+import { cvtStateToHashedRoutes, cvtFullRouteToState } from "./util";
+
+@Injectable({
+  providedIn: 'root'
+})
+
+export class RouterService {
+
+  private logError(...e: any[]) {
+    console.log(...e)
+  }
+
+  constructor(
+    router: Router,
+    pureConstantService: PureContantService,
+    store$: Store<any>,
+    @Inject(APP_BASE_HREF) baseHref: string
+  ){
+
+    // could be navigation (history api)
+    // could be on init
+    const navEnd$ = router.events.pipe(
+      filter(ev => ev instanceof NavigationEnd),
+      shareReplay(1)
+    )
+
+    navEnd$.subscribe()
+
+    const ready$ = pureConstantService.allFetchingReady$.pipe(
+      filter(flag => !!flag),
+      take(1),
+      shareReplay(1),
+    )
+
+    ready$.pipe(
+      switchMapTo(
+        navEnd$.pipe(
+          withLatestFrom(store$)
+        )
+      )
+    ).subscribe(([ev, state]: [NavigationEnd, any]) => {
+      const fullPath = ev.urlAfterRedirects
+      const stateFromRoute = cvtFullRouteToState(router.parseUrl(fullPath), state, this.logError)
+      let routeFromState: string
+      try {
+        routeFromState = cvtStateToHashedRoutes(state)
+      } catch (_e) {
+        routeFromState = ``
+      }
+
+      if ( fullPath !== `/${routeFromState}`) {
+        store$.dispatch(
+          generalApplyState({
+            state: stateFromRoute
+          })
+        )
+      }
+    })
+    
+    // TODO this may still be a bit finiky. 
+    // we rely on that update of store happens within 160ms
+    // which may or many not be 
+    ready$.pipe(
+      switchMapTo(
+        store$.pipe(
+          debounceTime(160),
+          map(state => {
+            try {
+              return cvtStateToHashedRoutes(state)
+            } catch (e) {
+              this.logError(e)
+              return ``
+            }
+          })
+        )
+      )
+    ).subscribe(routePath => {
+      if (routePath === '') {
+        router.navigate([ baseHref ])
+      } else {
+
+        // this needs to be done, because, for some silly reasons
+        // router decodes encoded ':' character
+        // this means, if url is compared with url, it will always be falsy
+        // if a non encoded ':' exists
+        const currUrlUrlTree = router.parseUrl(router.url)
+        const joinedRoutes = `/${routePath}`
+        const newUrlUrlTree = router.parseUrl(joinedRoutes)
+        
+        if (currUrlUrlTree.toString() !== newUrlUrlTree.toString()) {
+          router.navigateByUrl(joinedRoutes)
+        }
+      }
+    })
+  }
+}
diff --git a/src/routerModule/type.ts b/src/routerModule/type.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fa8855ad8202348ddbec659db149c84296e49a7c
--- /dev/null
+++ b/src/routerModule/type.ts
@@ -0,0 +1,34 @@
+export type TUrlStandaloneVolume<T> = {
+  sv: T // standalone volume
+}
+
+export type TUrlAtlas<T> = {
+  a: T   // atlas
+  t: T   // template
+  p: T   // parcellation
+  r?: T  // region selected
+}
+
+export type TUrlPreviewDs<T> = {
+  dsp: T // dataset preview
+}
+
+export type TUrlPlugin<T> = {
+  pl: T  // pluginState
+}
+
+export type TUrlNav<T> = {
+  ['@']: T // navstring
+}
+
+export type TConditional<T> = Partial<
+  TUrlPreviewDs<T> &
+  TUrlPlugin<T> &
+  TUrlNav<T>
+>
+
+export type TUrlPathObj<T, V> = 
+  (V extends TUrlStandaloneVolume<T>
+      ? TUrlStandaloneVolume<T>
+      : TUrlAtlas<T>)
+  & TConditional<T>
diff --git a/src/routerModule/util.spec.ts b/src/routerModule/util.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ac15ff5981b022ed662be9c7796e07fff9e941a0
--- /dev/null
+++ b/src/routerModule/util.spec.ts
@@ -0,0 +1,112 @@
+import { TestBed } from '@angular/core/testing'
+import { MockStore, provideMockStore } from '@ngrx/store/testing'
+import { uiStatePreviewingDatasetFilesSelector } from 'src/services/state/uiState/selectors'
+import { viewerStateGetSelectedAtlas, viewerStateSelectedParcellationSelector, viewerStateSelectedRegionsSelector, viewerStateSelectedTemplateSelector, viewerStateSelectorNavigation, viewerStateSelectorStandaloneVolumes } from 'src/services/state/viewerState/selectors'
+import { cvtFullRouteToState, cvtStateToHashedRoutes, DummyCmp, routes } from './util'
+import { encodeNumber } from './cipher'
+import { Router } from '@angular/router'
+import { RouterTestingModule } from '@angular/router/testing'
+
+describe('> util.ts', () => {
+  describe('> cvtFullRouteToState', () => {
+
+    beforeEach(() => {
+      TestBed.configureTestingModule({
+        imports: [
+          RouterTestingModule.withRoutes(routes, {
+            useHash: true
+          })
+        ],
+        declarations: [
+          DummyCmp
+        ]
+      })
+    })
+    beforeEach(() => {
+    })
+    it('> should be able to decode region properly', () => {
+
+    })
+
+    describe('> decode sv', () => {
+      let sv: any
+      beforeEach(() => {
+        const searchParam = new URLSearchParams()
+        searchParam.set('standaloneVolumes', '["precomputed://https://object.cscs.ch/v1/AUTH_08c08f9f119744cbbf77e216988da3eb/imgsvc-46d9d64f-bdac-418e-a41b-b7f805068c64"]')
+        const svRoute = `/?${searchParam.toString()}`
+        const router = TestBed.inject(Router)
+        const parsedUrl = router.parseUrl(svRoute)
+        const returnState = cvtFullRouteToState(parsedUrl, {})
+        sv = returnState?.viewerState?.standaloneVolumes
+      })
+
+      it('> sv should be truthy', () => {
+        expect(sv).toBeTruthy()
+      })
+
+      it('> sv should be array', () => {
+        expect(
+          Array.isArray(sv)
+        ).toBeTrue()
+      })
+
+      it('> sv should have length 1', () => {
+        expect(sv.length).toEqual(1)
+      })
+
+      it('> sv[0] should be expected value', () => {
+        expect(sv[0]).toEqual(
+          'precomputed://https://object.cscs.ch/v1/AUTH_08c08f9f119744cbbf77e216988da3eb/imgsvc-46d9d64f-bdac-418e-a41b-b7f805068c64'
+        )
+      })
+    })
+
+  })
+
+  describe('> cvtStateToHashedRoutes', () => {
+    let mockStore: MockStore
+    beforeEach(() => {
+      TestBed.configureTestingModule({
+        providers: [
+          provideMockStore()
+        ]
+      })
+
+      mockStore = TestBed.inject(MockStore)
+      
+      mockStore.overrideSelector(viewerStateGetSelectedAtlas, null)
+      mockStore.overrideSelector(viewerStateSelectedTemplateSelector, null)
+      mockStore.overrideSelector(viewerStateSelectedParcellationSelector, null)
+      mockStore.overrideSelector(viewerStateSelectedRegionsSelector, [])
+      mockStore.overrideSelector(viewerStateSelectorStandaloneVolumes, [])
+      mockStore.overrideSelector(viewerStateSelectorNavigation, null)
+
+      mockStore.overrideSelector(uiStatePreviewingDatasetFilesSelector, [])
+    })
+    describe('> should be able encode region properly', () => {
+
+      it('> regular ngId', () => {
+        const ngId = 'foobar'
+        const labelIndex = 124
+        mockStore.overrideSelector(viewerStateSelectedRegionsSelector, [{
+          labelIndex,
+          ngId
+        }])
+        const s = cvtStateToHashedRoutes({})
+        expect(s).toContain(`r:${ngId}::${encodeNumber(labelIndex, { float: false })}`)
+      })
+
+      it('> ngId containing ()', () => {
+
+        const ngId = 'foobar(1)'
+        const labelIndex = 124
+        mockStore.overrideSelector(viewerStateSelectedRegionsSelector, [{
+          labelIndex,
+          ngId
+        }])
+        const s = cvtStateToHashedRoutes({})
+        expect(s).toContain(`r:foobar%25281%2529::${encodeNumber(labelIndex, { float: false })}`)
+      })
+    })
+  })
+})
diff --git a/src/routerModule/util.ts b/src/routerModule/util.ts
new file mode 100644
index 0000000000000000000000000000000000000000..224458c860b9ae37073a4ee40a8dd839e7f3ddca
--- /dev/null
+++ b/src/routerModule/util.ts
@@ -0,0 +1,298 @@
+import { viewerStateGetSelectedAtlas, viewerStateSelectedParcellationSelector, viewerStateSelectedRegionsSelector, viewerStateSelectedTemplateSelector, viewerStateSelectorNavigation, viewerStateSelectorStandaloneVolumes } from "src/services/state/viewerState/selectors"
+import { encodeNumber, decodeToNumber, separator, encodeURIFull } from './cipher'
+import { UrlSegment, UrlTree } from "@angular/router"
+import { getShader, PMAP_DEFAULT_CONFIG } from "src/util/constants"
+import { mixNgLayers } from "src/services/state/ngViewerState.store"
+import { PLUGINSTORE_CONSTANTS } from 'src/services/state/pluginState.helper'
+import { viewerStateHelperStoreName } from "src/services/state/viewerState.store.helper"
+import { uiStatePreviewingDatasetFilesSelector } from "src/services/state/uiState/selectors"
+import { Component } from "@angular/core"
+
+import {
+  TUrlStandaloneVolume,
+  TUrlAtlas,
+  TUrlPathObj,
+} from './type'
+
+import {
+  parseSearchParamForTemplateParcellationRegion,
+  encodeId,
+} from './parseRouteToTmplParcReg'
+
+const endcodePath = (key: string, val: string|string[]) =>
+  key[0] === '?'
+    ? `?${key}=${val}`
+    : `${key}:${Array.isArray(val)
+      ? val.map(v => encodeURI(v)).join('::')
+      : encodeURI(val)}`
+const decodePath = (path: string) => {
+  const re = /^(.*?):(.*?)$/.exec(path)
+  if (!re) return null
+  return {
+    key: re[1],
+    val: re[2].split('::').map(v => decodeURI(v))
+  }
+}
+
+export const cvtFullRouteToState = (fullPath: UrlTree, state: any, _warnCb?: Function) => {
+
+  const warnCb = _warnCb || ((...e: any[]) => console.warn(...e))
+  const pathFragments: UrlSegment[] = fullPath.root.hasChildren()
+    ? fullPath.root.children['primary'].segments
+    : []
+
+  const returnState = JSON.parse(JSON.stringify(state))
+
+  const returnObj: Partial<TUrlPathObj<string[], unknown>> = {}
+  for (const f of pathFragments) {
+    const { key, val } = decodePath(f.path) || {}
+    if (!key || !val) continue
+    returnObj[key] = val
+  }
+
+  // TODO deprecate
+  // but ensure bkwd compat?
+  const niftiLayers = fullPath.queryParams['niftiLayers']
+  if (niftiLayers) {
+    const layers = niftiLayers
+      .split('__')
+      .map(layer => {
+        return {
+          name : layer,
+          source : `nifti://${layer}`,
+          mixability : 'nonmixable',
+          shader : getShader(PMAP_DEFAULT_CONFIG),
+        } as any
+      })
+    const { ngViewerState } = returnState
+    ngViewerState.layers = mixNgLayers(ngViewerState.layers, layers)
+  }
+  // -- end deprecate
+
+  // logical assignment. Use instead of above after typescript > v4.0.0
+  // returnState['viewerState'] ||= {}
+  if (!returnState['viewerState']) {
+    returnState['viewerState'] = {}
+  }
+  // -- end fix logical assignment
+
+  // nav obj is almost always defined, regardless if standaloneVolume or not
+  const cViewerState = returnObj['@'] && returnObj['@'][0]
+  let parsedNavObj = {}
+  if (cViewerState) {
+    try {
+      const [ cO, cPO, cPZ, cP, cZ ] = cViewerState.split(`${separator}${separator}`)
+      const o = cO.split(separator).map(s => decodeToNumber(s, {float: true}))
+      const po = cPO.split(separator).map(s => decodeToNumber(s, {float: true}))
+      const pz = decodeToNumber(cPZ)
+      const p = cP.split(separator).map(s => decodeToNumber(s))
+      const z = decodeToNumber(cZ)
+      parsedNavObj = {
+        orientation: o,
+        perspectiveOrientation: po,
+        perspectiveZoom: pz,
+        position: p,
+        zoom: z,
+
+        // flag to allow for animation when enabled
+        animation: {},
+      }
+    } catch (e) {
+      /**
+       * TODO Poisoned encoded char
+       * send error message
+       */
+      warnCb(`parse nav error`, e)
+    }
+  }
+
+  // pluginState should always be defined, regardless if standalone volume or not
+  const pluginStates = fullPath.queryParams['pl']
+  const { pluginState } = returnState
+  if (pluginStates) {
+    try {
+      const arrPluginStates = JSON.parse(pluginStates)
+      pluginState.initManifests = arrPluginStates.map(url => [PLUGINSTORE_CONSTANTS.INIT_MANIFEST_SRC, url] as [string, string])
+    } catch (e) {
+      /**
+       * parsing plugin error
+       */
+      warnCb(`parse plugin states error`, e, pluginStates)
+    }
+  }
+
+  // preview dataset can and should be displayed regardless of standalone volume or not
+
+  try {
+    const { uiState } = returnState
+    const arr = returnObj.dsp
+      ? [{
+        datasetId: returnObj.dsp[0],
+        filename: returnObj.dsp[1]
+      }]
+      : fullPath.queryParams['previewingDatasetFiles'] && JSON.parse(fullPath.queryParams['previewingDatasetFiles'])
+    if (arr) {
+      uiState.previewingDatasetFiles = arr.map(({ datasetId, filename }) => {
+        return {
+          datasetId,
+          filename
+        }
+      })
+    }
+  } catch (e) {
+    // parsing previewingDatasetFiles
+    warnCb(`parse dsp error`, e)
+  }
+
+  // If sv (standaloneVolume is defined)
+  // only load sv in state
+  // ignore all other params
+  // /#/sv:%5B%22precomputed%3A%2F%2Fhttps%3A%2F%2Fobject.cscs.ch%2Fv1%2FAUTH_08c08f9f119744cbbf77e216988da3eb%2Fimgsvc-46d9d64f-bdac-418e-a41b-b7f805068c64%22%5D
+  const standaloneVolumes = fullPath.queryParams['standaloneVolumes']
+  if (!!standaloneVolumes) {
+    try {
+      const parsedArr = JSON.parse(standaloneVolumes)
+      if (!Array.isArray(parsedArr)) throw new Error(`Parsed standalone volumes not of type array`)
+
+      returnState['viewerState']['standaloneVolumes'] = parsedArr
+      returnState['viewerState']['navigation'] = parsedNavObj
+      return returnState
+    } catch (e) {
+      // if any error occurs, parse rest per normal
+      warnCb(`parse standalone volume error`, e)
+    }
+  } else {
+    returnState['viewerState']['standaloneVolumes'] = []
+  }
+
+  try {
+    const { parcellationSelected, regionsSelected, templateSelected } = parseSearchParamForTemplateParcellationRegion(returnObj as TUrlPathObj<string[], TUrlAtlas<string[]>>, fullPath, state, warnCb)
+    returnState['viewerState']['parcellationSelected'] = parcellationSelected
+    returnState['viewerState']['regionsSelected'] = regionsSelected
+    returnState['viewerState']['templateSelected'] = templateSelected
+
+    returnState['viewerState']['navigation'] = parsedNavObj
+  } catch (e) {
+    // if error, show error on UI?
+    warnCb(`parse template, parc, region error`, e)
+  }
+
+  /**
+   * parsing template to get atlasId
+   */
+  (() => {
+
+    const viewreHelperState = returnState[viewerStateHelperStoreName] || {}
+    const { templateSelected, parcellationSelected } = returnState['viewerState']
+    const { fetchedAtlases, ...rest } = viewreHelperState
+    
+    const selectedAtlas = (fetchedAtlases || []).find(a => a['templateSpaces'].find(t => t['@id'] === (templateSelected && templateSelected['@id'])))
+    
+    const overlayLayer = selectedAtlas && selectedAtlas['parcellations'].find(p => p['@id'] === (parcellationSelected && parcellationSelected['@id']))
+
+    viewreHelperState['selectedAtlasId'] = selectedAtlas && selectedAtlas['@id']
+    viewreHelperState['overlayingAdditionalParcellations'] = (overlayLayer && !overlayLayer['baseLayer'])
+      ? [ overlayLayer ]
+      : []
+  })()
+
+  return returnState
+}
+
+export const cvtStateToHashedRoutes = (state): string => {
+  // TODO check if this causes memleak
+  const selectedAtlas = viewerStateGetSelectedAtlas(state)
+  const selectedTemplate = viewerStateSelectedTemplateSelector(state)
+  const selectedParcellation = viewerStateSelectedParcellationSelector(state)
+  const selectedRegions = viewerStateSelectedRegionsSelector(state)
+  const standaloneVolumes = viewerStateSelectorStandaloneVolumes(state)
+  const navigation = viewerStateSelectorNavigation(state)
+
+  const previewingDatasetFiles = uiStatePreviewingDatasetFilesSelector(state)
+  let dsPrvString: string
+  const searchParam = new URLSearchParams()
+
+  if (previewingDatasetFiles && Array.isArray(previewingDatasetFiles)) {
+    const dsPrvArr = []
+    const datasetPreviews = (previewingDatasetFiles as {datasetId: string, filename: string}[])
+    for (const preview of datasetPreviews) {
+      dsPrvArr.push(preview)
+    }
+
+    if (dsPrvArr.length === 1) {
+      dsPrvString = `${dsPrvArr[0].datasetId}::${dsPrvArr[0].filename}`
+    }
+  }
+
+  let cNavString: string
+  if (navigation) {
+    const { orientation, perspectiveOrientation, perspectiveZoom, position, zoom } = navigation
+    if (orientation && perspectiveOrientation && perspectiveZoom && position && zoom) {
+      cNavString = [
+        orientation.map((n: number) => encodeNumber(n, {float: true})).join(separator),
+        perspectiveOrientation.map(n => encodeNumber(n, {float: true})).join(separator),
+        encodeNumber(Math.floor(perspectiveZoom)),
+        Array.from(position).map((v: number) => Math.floor(v)).map(n => encodeNumber(n)).join(separator),
+        encodeNumber(Math.floor(zoom)),
+      ].join(`${separator}${separator}`)
+    }
+  }
+
+  // encoding selected regions
+  let selectedRegionsString
+  if (selectedRegions.length === 1) {
+    const region = selectedRegions[0]
+    const { ngId, labelIndex } = region
+    selectedRegionsString = `${ngId}::${encodeNumber(labelIndex, { float: false })}`
+  }
+  let routes: any
+  
+  routes= {
+    // for atlas
+    a: selectedAtlas && encodeId(selectedAtlas['@id']),
+    // for template
+    t: selectedTemplate && encodeId(selectedTemplate['@id'] || selectedTemplate['fullId']),
+    // for parcellation
+    p: selectedParcellation && encodeId(selectedParcellation['@id'] || selectedParcellation['fullId']),
+    // for regions
+    r: selectedRegionsString && encodeURIFull(selectedRegionsString),
+    // nav
+    ['@']: cNavString,
+    // dataset file preview
+    dsp: dsPrvString && encodeURI(dsPrvString),
+  } as TUrlPathObj<string, TUrlAtlas<string>>
+
+  /**
+   * if any params needs to overwrite previosu routes, put them here
+   */
+  if (standaloneVolumes && Array.isArray(standaloneVolumes) && standaloneVolumes.length > 0) {
+    searchParam.set('standaloneVolumes', JSON.stringify(standaloneVolumes))
+    routes = {
+      // nav
+      ['@']: cNavString,
+      dsp: dsPrvString && encodeURI(dsPrvString)
+    } as TUrlPathObj<string|string[], TUrlStandaloneVolume<string[]>>
+  }
+
+  const routesArr: string[] = []
+  for (const key in routes) {
+    if (!!routes[key]) {
+      const segStr = endcodePath(key, routes[key])
+      routesArr.push(segStr)
+    }
+  }
+
+  return searchParam.toString() === '' 
+    ? routesArr.join('/')
+    : `${routesArr.join('/')}?${searchParam.toString()}`
+}
+
+@Component({
+  template: ''
+})
+export class DummyCmp{}
+
+export const routes = [{
+  path: '**',
+  component: DummyCmp
+}]
diff --git a/src/services/dialogService.service.ts b/src/services/dialogService.service.ts
index 05911fdc7eb56741c4789f666d8c39ce90be1dd9..c202eda5884ab18cc47d194d931504e19af11202 100644
--- a/src/services/dialogService.service.ts
+++ b/src/services/dialogService.service.ts
@@ -63,5 +63,6 @@ export interface DialogConfig {
   placeholder: string
   defaultValue: string
   message: string
+  markdown?: string
   iconClass: string
 }
diff --git a/src/services/effect/effect.spec.ts b/src/services/effect/effect.spec.ts
index a414cb18e56ecaabce80113fb72a096f2819a4d6..906e23daf3f5d76b601306fa10fff91e6c912611 100644
--- a/src/services/effect/effect.spec.ts
+++ b/src/services/effect/effect.spec.ts
@@ -1,34 +1,15 @@
 import {} from 'jasmine'
-import { getGetRegionFromLabelIndexId, UseEffects } from './effect'
+import { UseEffects } from './effect'
 import { TestBed } from '@angular/core/testing'
 import { Observable } from 'rxjs'
-import { SELECT_PARCELLATION, NEWVIEWER, SELECT_REGIONS } from '../state/viewerState.store'
+import { SELECT_PARCELLATION, SELECT_REGIONS } from '../state/viewerState.store'
 import { provideMockActions } from '@ngrx/effects/testing'
 import { hot } from 'jasmine-marbles'
 import { provideMockStore } from '@ngrx/store/testing'
 import { defaultRootState } from '../stateStore.service'
-
-const colinsJson = require('!json-loader!../../res/ext/colin.json')
-
-const COLIN_JULICHBRAIN_LAYER_NAME = `COLIN_V25_LEFT_NG_SPLIT_HEMISPHERE`
-const COLIN_V25_ID = 'minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-25'
+import { viewerStateNewViewer } from '../state/viewerState/actions'
 
 describe('effect.ts', () => {
-  describe('getGetRegionFromLabelIndexId', () => {
-    it('translateds hoc1 from labelIndex to region', () => {
-
-      const getRegionFromlabelIndexId = getGetRegionFromLabelIndexId({
-        parcellation: {
-          ...colinsJson.parcellations.find(p => p['@id'] === COLIN_V25_ID),
-          updated: true,
-        },
-      })
-      const fetchedRegion = getRegionFromlabelIndexId({ labelIndexId: `${COLIN_JULICHBRAIN_LAYER_NAME}#116` })
-      expect(fetchedRegion).toBeTruthy()
-      expect(fetchedRegion.fullId.kg.kgId).toEqual('c9753e82-80ca-4074-a704-9dd2c4c0d58b')
-      
-    })
-  })
 
   describe('UseEffects', () => {
     let actions$:Observable<any>
@@ -43,13 +24,13 @@ describe('effect.ts', () => {
       })
     })
 
-    it('both SELECT_PARCELLATION and NEWVIEWER actions should trigger onParcellationSelected$', () => {
+    it('both SELECT_PARCELLATION and viewerStateNewViewer.type actions should trigger onParcellationSelected$', () => {
       const useEffectsInstance: UseEffects = TestBed.inject(UseEffects)
       actions$ = hot(
         'ab',
         {
           a: { type: SELECT_PARCELLATION },
-          b: { type: NEWVIEWER }
+          b: { type: viewerStateNewViewer.type }
         }
       )
       expect(
diff --git a/src/services/effect/effect.ts b/src/services/effect/effect.ts
index c206014533f352a8530b8b232b2b96141a05b8e8..10e5506143f0963ddaea23b81bd3e09509c8623b 100644
--- a/src/services/effect/effect.ts
+++ b/src/services/effect/effect.ts
@@ -4,9 +4,11 @@ import { select, Store } from "@ngrx/store";
 import { merge, Observable, Subscription, combineLatest } from "rxjs";
 import { filter, map, shareReplay, switchMap, take, withLatestFrom, mapTo, distinctUntilChanged } from "rxjs/operators";
 import { LoggingService } from "src/logging";
-import { ADD_TO_REGIONS_SELECTION_WITH_IDS, DESELECT_REGIONS, NEWVIEWER, SELECT_PARCELLATION, SELECT_REGIONS, SELECT_REGIONS_WITH_ID, SELECT_LANDMARKS } from "../state/viewerState.store";
-import { generateLabelIndexId, getNgIdLabelIndexFromId, IavRootStoreInterface, recursiveFindRegionWithLabelIndexId } from '../stateStore.service';
-import { viewerStateSelectAtlas, viewerStateSetSelectedRegionsWithIds, viewerStateToggleLayer } from "../state/viewerState.store.helper";
+import { IavRootStoreInterface, recursiveFindRegionWithLabelIndexId } from '../stateStore.service';
+import { viewerStateNewViewer, viewerStateSelectAtlas, viewerStateSetSelectedRegionsWithIds, viewerStateToggleLayer } from "../state/viewerState.store.helper";
+import { deserialiseParcRegionId, serialiseParcellationRegion } from "common/util"
+import { getGetRegionFromLabelIndexId } from 'src/util/fn'
+import { actionAddToRegionsSelectionWithIds, actionSelectLandmarks, viewerStateSelectParcellation, viewerStateSelectRegionWithIdDeprecated, viewerStateSetSelectedRegions } from "../state/viewerState/actions";
 
 @Injectable({
   providedIn: 'root',
@@ -51,26 +53,10 @@ export class UseEffects implements OnDestroy {
           /**
            * only allow 1 selection at a time
            */
-        return {
-          type: SELECT_REGIONS,
+        return viewerStateSetSelectedRegions({
           selectRegions: selectRegions.slice(0,1)
-        }
-      })
-    )
-    
-    this.onDeselectRegions = this.actions$.pipe(
-      ofType(DESELECT_REGIONS),
-      withLatestFrom(this.regionsSelected$),
-      map(([action, regionsSelected]) => {
-        const { deselectRegions } = action
-        const selectRegions = regionsSelected.filter(r => {
-          return !(deselectRegions as any[]).find(dr => compareRegions(dr, r))
         })
-        return {
-          type: SELECT_REGIONS,
-          selectRegions,
-        }
-      }),
+      })
     )
 
     this.onDeselectRegionsWithId$ = this.actions$.pipe(
@@ -82,16 +68,15 @@ export class UseEffects implements OnDestroy {
       withLatestFrom(this.regionsSelected$),
       map(([ deselecRegionIds, alreadySelectedRegions ]) => {
         const deselectSet = new Set(deselecRegionIds)
-        return {
-          type: SELECT_REGIONS,
+        return viewerStateSetSelectedRegions({
           selectRegions: alreadySelectedRegions
-            .filter(({ ngId, labelIndex }) => !deselectSet.has(generateLabelIndexId({ ngId, labelIndex }))),
-        }
+            .filter(({ ngId, labelIndex }) => !deselectSet.has(serialiseParcellationRegion({ ngId, labelIndex }))),
+        })
       }),
     )
 
     this.addToSelectedRegions$ = this.actions$.pipe(
-      ofType(ADD_TO_REGIONS_SELECTION_WITH_IDS),
+      ofType(actionAddToRegionsSelectionWithIds.type),
       map(action => {
         const { selectRegionIds } = action
         return selectRegionIds
@@ -104,10 +89,9 @@ export class UseEffects implements OnDestroy {
       map(this.convertRegionIdsToRegion),
       withLatestFrom(this.regionsSelected$),
       map(([ selectedRegions, alreadySelectedRegions ]) => {
-        return {
-          type: SELECT_REGIONS,
+        return viewerStateSetSelectedRegions({
           selectRegions: this.removeDuplicatedRegions(selectedRegions, alreadySelectedRegions),
-        }
+        })
       }),
     )
   }
@@ -123,7 +107,7 @@ export class UseEffects implements OnDestroy {
   private subscriptions: Subscription[] = []
 
   private parcellationSelected$ = this.actions$.pipe(
-    ofType(SELECT_PARCELLATION),
+    ofType(viewerStateSelectParcellation.type),
   )
 
 
@@ -134,19 +118,16 @@ export class UseEffects implements OnDestroy {
     shareReplay(1),
   )
 
-  @Effect()
-  public onDeselectRegions: Observable<any>
-
   @Effect()
   public onDeselectRegionsWithId$: Observable<any>
 
   private convertRegionIdsToRegion = ([selectRegionIds, parcellation]) => {
     const { ngId: defaultNgId } = parcellation
     return (selectRegionIds as any[])
-      .map(labelIndexId => getNgIdLabelIndexFromId({ labelIndexId }))
+      .map(labelIndexId => deserialiseParcRegionId(labelIndexId))
       .map(({ ngId, labelIndex }) => {
         return {
-          labelIndexId: generateLabelIndexId({
+          labelIndexId: serialiseParcellationRegion({
             ngId: ngId || defaultNgId,
             labelIndex,
           }),
@@ -190,7 +171,7 @@ export class UseEffects implements OnDestroy {
    */
   @Effect()
   public onSelectRegionWithId = this.actions$.pipe(
-    ofType(SELECT_REGIONS_WITH_ID),
+    ofType(viewerStateSelectRegionWithIdDeprecated.type),
     map(action => {
       const { selectRegionIds } = action
       return selectRegionIds
@@ -202,10 +183,9 @@ export class UseEffects implements OnDestroy {
     )),
     map(this.convertRegionIdsToRegion),
     map(selectRegions => {
-      return {
-        type: SELECT_REGIONS,
-        selectRegions,
-      }
+      return viewerStateSetSelectedRegions({
+        selectRegions
+      })
     }),
   )
 
@@ -219,16 +199,17 @@ export class UseEffects implements OnDestroy {
     ),
     this.parcellationSelected$,
     this.actions$.pipe(
-      ofType(NEWVIEWER)
+      ofType(viewerStateNewViewer.type)
     ),
     this.actions$.pipe(
-      ofType(viewerStateSelectAtlas)
+      ofType(viewerStateSelectAtlas.type)
     )
   ).pipe(
-    mapTo({
-      type: SELECT_REGIONS,
-      selectRegions: [],
-    })
+    mapTo(
+      viewerStateSetSelectedRegions({
+        selectRegions: []
+      })
+    )
   )
 
   /**
@@ -238,21 +219,15 @@ export class UseEffects implements OnDestroy {
 
   @Effect()
   public onNewViewerResetLandmarkSelected$ = this.actions$.pipe(
-    ofType(NEWVIEWER),
-    mapTo({
-      type: SELECT_LANDMARKS,
-      landmarks: []
-    })
+    ofType(viewerStateNewViewer.type),
+    mapTo(
+      actionSelectLandmarks({
+        landmarks: []
+      })
+    )
   )
 }
 
-export const getGetRegionFromLabelIndexId = ({ parcellation }) => {
-  const { ngId: defaultNgId, regions } = parcellation
-  // if (!updated) throw new Error(`parcellation not yet updated`)
-  return ({ labelIndexId }) =>
-    recursiveFindRegionWithLabelIndexId({ regions, labelIndexId, inheritedNgId: defaultNgId })
-}
-
 export const compareRegions: (r1: any, r2: any) => boolean = (r1, r2) => {
   if (!r1) { return !r2 }
   if (!r2) { return !r1 }
diff --git a/src/services/effect/newTemplate.effect.ts b/src/services/effect/newTemplate.effect.ts
index d6b68b5d1c3b15fa0877054c1e4cae31ca99024f..f2c2541fe6fee218be482ad7f67b0b84443a8928 100644
--- a/src/services/effect/newTemplate.effect.ts
+++ b/src/services/effect/newTemplate.effect.ts
@@ -1,9 +1,9 @@
 import { Injectable } from "@angular/core";
 import { Actions, Effect, ofType } from "@ngrx/effects";
 import { Observable } from "rxjs";
-import { NEWVIEWER } from "../state/viewerState.store";
-import { mapTo, tap } from "rxjs/operators";
+import { mapTo } from "rxjs/operators";
 import { DATASETS_ACTIONS_TYPES } from "../state/dataStore.store";
+import { viewerStateNewViewer } from "../state/viewerState/actions";
 
 @Injectable({
   providedIn: 'root'
@@ -18,7 +18,7 @@ export class NewTemplateUseEffect{
     private actions$: Actions
   ){
     this.onNewTemplateShouldClearPreviewDataset$ = this.actions$.pipe(
-      ofType(NEWVIEWER),
+      ofType(viewerStateNewViewer.type),
       mapTo({
         type: DATASETS_ACTIONS_TYPES.CLEAR_PREVIEW_DATASETS
       })
diff --git a/src/services/effect/pluginUseEffect.spec.ts b/src/services/effect/pluginUseEffect.spec.ts
index 2bd9efda5c25597914f82dcaf0acbd38fa18f288..e89947a19bd3544260c890b369c69ab92ad7d732 100644
--- a/src/services/effect/pluginUseEffect.spec.ts
+++ b/src/services/effect/pluginUseEffect.spec.ts
@@ -6,12 +6,13 @@ import { Action } from "@ngrx/store";
 import { provideMockActions } from "@ngrx/effects/testing";
 import { provideMockStore } from "@ngrx/store/testing";
 import { defaultRootState } from "../stateStore.service";
-import { PLUGINSTORE_CONSTANTS, PLUGINSTORE_ACTION_TYPES } from '../state/pluginState.store'
+import { PLUGINSTORE_CONSTANTS, PLUGINSTORE_ACTION_TYPES } from '../state/pluginState.helper'
 import { Injectable } from "@angular/core";
 import { getRandomHex } from 'common/util'
-import { PluginServices } from "src/atlasViewer/pluginUnit";
+import { PluginServices } from "src/plugin";
 import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module";
 import { hot } from "jasmine-marbles";
+import { BS_ENDPOINT } from "src/util/constants";
 
 const actions$: Observable<Action> = of({type: 'TEST'})
 
@@ -102,6 +103,10 @@ describe('pluginUseEffect.ts', () => {
         {
           provide: PluginServices,
           useClass: MockPluginService
+        },
+        {
+          provide: BS_ENDPOINT,
+          useValue: `http://localhost:1234`
         }
       ]
     }).compileComponents()
diff --git a/src/services/effect/pluginUseEffect.ts b/src/services/effect/pluginUseEffect.ts
index 1c60a3d5a99fb8a758aeeaf318308330cf23794f..bd0a74772c60d75cceda439cb5402df584c4e2af 100644
--- a/src/services/effect/pluginUseEffect.ts
+++ b/src/services/effect/pluginUseEffect.ts
@@ -3,11 +3,10 @@ import { Effect } from "@ngrx/effects"
 import { select, Store } from "@ngrx/store"
 import { Observable, forkJoin } from "rxjs"
 import { filter, map, startWith, switchMap } from "rxjs/operators"
-import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service"
-import { PluginServices } from "src/atlasViewer/pluginUnit"
-import { PLUGINSTORE_ACTION_TYPES, PLUGINSTORE_CONSTANTS } from 'src/services/state/pluginState.store'
-import { IavRootStoreInterface } from "../stateStore.service"
+import { PluginServices } from "src/plugin/atlasViewer.pluginService.service"
+import { PLUGINSTORE_CONSTANTS, PLUGINSTORE_ACTION_TYPES, pluginStateSelectorInitManifests } from 'src/services/state/pluginState.helper'
 import { HttpClient } from "@angular/common/http"
+import { getHttpHeader } from "src/util/constants"
 
 @Injectable({
   providedIn: 'root',
@@ -19,15 +18,12 @@ export class PluginServiceUseEffect {
   public initManifests$: Observable<any>
 
   constructor(
-    store$: Store<IavRootStoreInterface>,
-    constantService: AtlasViewerConstantsServices,
+    store$: Store<any>,
     pluginService: PluginServices,
     http: HttpClient
   ) {
     this.initManifests$ = store$.pipe(
-      select('pluginState'),
-      select('initManifests'),
-      filter(v => !!v),
+      select(pluginStateSelectorInitManifests),
       startWith([]),
       map(arr => {
         // only launch plugins that has init manifest src label on it
@@ -35,9 +31,9 @@ export class PluginServiceUseEffect {
       }),
       filter(arr => arr.length > 0),
       switchMap(arr => forkJoin(
-        ...arr.map(([_source, url]) => 
+        arr.map(([_source, url]) => 
           http.get(url, {
-            headers: constantService.getHttpHeader(),
+            headers: getHttpHeader(),
             responseType: 'json'
           })
         )
diff --git a/src/services/localFile.service.ts b/src/services/localFile.service.ts
index d4386edeca620ea7fc2ded16cd54623a265b6c97..045ac686b54f7b65e2985f7d75c2bce85ce705c6 100644
--- a/src/services/localFile.service.ts
+++ b/src/services/localFile.service.ts
@@ -1,6 +1,5 @@
 import { Injectable } from "@angular/core";
 import { Store } from "@ngrx/store";
-import { KgSingleDatasetService } from "src/ui/databrowserModule/kgSingleDatasetService.service";
 import { SNACKBAR_MESSAGE } from "./state/uiState.store";
 import { IavRootStoreInterface } from "./stateStore.service";
 import { DATASETS_ACTIONS_TYPES } from "./state/dataStore.store";
@@ -19,12 +18,11 @@ export class LocalFileService {
 
   constructor(
     private store: Store<IavRootStoreInterface>,
-    private singleDsService: KgSingleDatasetService,
   ) {
 
   }
 
-  private niiUrl
+  private niiUrl: string
 
   public handleFileDrop(files: File[]) {
     try {
diff --git a/src/services/state/dataState/actions.ts b/src/services/state/dataState/actions.ts
index 22560c293cd4134629a85406686158c8873e63ac..495a623165201b0012b61a9c4397226a9502e83e 100644
--- a/src/services/state/dataState/actions.ts
+++ b/src/services/state/dataState/actions.ts
@@ -1,4 +1,5 @@
 import { createAction, props } from "@ngrx/store";
+import { IKgDataEntry } from "src/atlasComponents/databrowserModule";
 
 export const datastateActionToggleFav = createAction(
   `[datastate] toggleFav`,
@@ -19,3 +20,8 @@ export const datastateActionFavDataset = createAction(
   `[datastate] fav`,
   props<{ payload: { fullId: string } }>()
 )
+
+export const datastateActionFetchedDataentries = createAction(
+  `[datastate] fetchedDatastate`,
+  props<{ fetchedDataEntries: IKgDataEntry[] }>()
+)
\ No newline at end of file
diff --git a/src/services/state/dataStore.store.ts b/src/services/state/dataStore.store.ts
index 42e389fcf2d4f1d6e462e583337c09e958005de1..962168dd111b4219e69c6f595f5ccf596df2fc95 100644
--- a/src/services/state/dataStore.store.ts
+++ b/src/services/state/dataStore.store.ts
@@ -3,10 +3,10 @@
  */
 
 import { Action } from '@ngrx/store'
-import { GENERAL_ACTION_TYPES } from '../stateStore.service'
 import { LOCAL_STORAGE_CONST } from 'src/util/constants'
-import { datastateActionUpdateFavDataset } from './dataState/actions'
+import { datastateActionFetchedDataentries, datastateActionUpdateFavDataset } from './dataState/actions'
 import { IHasId } from 'src/util/interfaces'
+import { generalApplyState } from '../stateStore.helper'
 
 /**
  * TODO merge with databrowser.usereffect.ts
@@ -44,6 +44,7 @@ export const defaultState = {
 export const getStateStore = ({ state: state = defaultState } = {}) => (prevState: IStateInterface = state, action: Partial<IActionInterface>) => {
 
   switch (action.type) {
+  case datastateActionFetchedDataentries.type:
   case FETCHED_DATAENTRIES: {
     return {
       ...prevState,
@@ -63,7 +64,7 @@ export const getStateStore = ({ state: state = defaultState } = {}) => (prevStat
       favDataEntries,
     }
   }
-  case GENERAL_ACTION_TYPES.APPLY_STATE: {
+  case generalApplyState.type: {
     const { dataStore } = (action as any).state
     return dataStore
   }
diff --git a/src/services/state/ngViewerState.store.helper.ts b/src/services/state/ngViewerState.store.helper.ts
index 2d0ca1500648a3654e7108cc7a22588055ac50ba..6f23c74d1c306a3b74e824fb69c7adc0743d0cc8 100644
--- a/src/services/state/ngViewerState.store.helper.ts
+++ b/src/services/state/ngViewerState.store.helper.ts
@@ -1,9 +1,7 @@
 // TODO to be merged with ng viewer state after refactor
-import { INgLayerInterface, PANELS } from './ngViewerState/constants'
+export { INgLayerInterface, PANELS } from './ngViewerState/constants'
 
-export { INgLayerInterface, PANELS }
-
-import {
+export {
   ngViewerActionAddNgLayer,
   ngViewerActionRemoveNgLayer,
   ngViewerActionSetPerspOctantRemoval,
@@ -14,29 +12,11 @@ import {
 } from './ngViewerState/actions'
 
 export {
-  ngViewerActionAddNgLayer,
-  ngViewerActionRemoveNgLayer,
-  ngViewerActionSetPerspOctantRemoval,
-  ngViewerActionToggleMax,
-  ngViewerActionClearView,
-  ngViewerActionSetPanelOrder,
-  ngViewerActionForceShowSegment,
-}
-
-import {
   ngViewerSelectorClearView,
   ngViewerSelectorClearViewEntries,
   ngViewerSelectorNehubaReady,
   ngViewerSelectorOctantRemoval,
   ngViewerSelectorPanelMode,
   ngViewerSelectorPanelOrder,
+  ngViewerSelectorLayers,
 } from './ngViewerState/selectors'
-
-export {
-  ngViewerSelectorClearView,
-  ngViewerSelectorClearViewEntries,
-  ngViewerSelectorNehubaReady,
-  ngViewerSelectorOctantRemoval,
-  ngViewerSelectorPanelMode,
-  ngViewerSelectorPanelOrder,
-}
\ No newline at end of file
diff --git a/src/services/state/ngViewerState.store.spec.ts b/src/services/state/ngViewerState.store.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..21173833cf134a9e53274c3017abf97bdef947fb
--- /dev/null
+++ b/src/services/state/ngViewerState.store.spec.ts
@@ -0,0 +1,100 @@
+import { HttpClientTestingModule, HttpTestingController } from "@angular/common/http/testing"
+import { TestBed } from "@angular/core/testing"
+import { provideMockActions } from "@ngrx/effects/testing"
+import { Action } from "@ngrx/store"
+import { provideMockStore } from "@ngrx/store/testing"
+import { Observable, of } from "rxjs"
+import { PureContantService } from "src/util"
+import { generalApplyState } from "../stateStore.helper"
+import { NgViewerUseEffect } from "./ngViewerState.store"
+
+const action$: Observable<Action> = of({ type: 'TEST'})
+const initState = {}
+describe('> ngViewerState.store.ts', () => {
+  describe('> NgViewerUseEffect', () => {
+    let ef: NgViewerUseEffect
+    beforeEach(() => {
+
+      TestBed.configureTestingModule({
+        imports: [
+          HttpClientTestingModule,
+        ],
+        providers: [
+          provideMockActions(() => action$),
+          provideMockStore({ initialState: initState }),
+          {
+            provide: PureContantService,
+            useValue: {
+              useTouchUI$: of(false),
+              backendUrl: `http://localhost:3000/`
+            }
+          }
+        ]
+      })
+    })
+
+    it('> shoudl be insable', () => {
+      ef = TestBed.inject(NgViewerUseEffect)
+      expect(ef).toBeTruthy()
+    })
+
+    describe('> applySavedUserConfig$', () => {
+
+      let ctrl: HttpTestingController
+      beforeEach(() => {
+        ctrl = TestBed.inject(HttpTestingController)
+        ef = TestBed.inject(NgViewerUseEffect)
+      })
+
+      afterEach(() => {
+        ctrl.verify()
+      })
+      
+      it('> if http response errors, user$ should be stream of null', () => {
+        
+        ef.applySavedUserConfig$.subscribe(_action => {
+          // should not emit
+          expect(false).toEqual(true)
+        })
+        const resp1 = ctrl.expectOne('http://localhost:3000/user/config')
+        resp1.error(null, {
+          status: 404,
+        })
+      })
+
+      it('> if http response contains truthy error key, user should return stream of null', () => {
+
+        ef.applySavedUserConfig$.subscribe(_action => {
+          // should not emit
+          expect(false).toEqual(true)
+        })
+        const resp = ctrl.expectOne('http://localhost:3000/user/config')
+        resp.flush({
+          error: true
+        })
+      })
+
+      it('> if http response does not contain error key, should return the resp', () => {
+        
+        const mUserState = {
+          foo: 'baz',
+          baz: 'pineablle'
+        }
+        ef.applySavedUserConfig$.subscribe(action => {
+          expect(action).toEqual(generalApplyState({
+            state: {
+              ...initState,
+              ngViewerState: {
+                ...(initState['ngViewerState'] || {}),
+                ...mUserState,
+              }
+            }
+          }))
+        })
+        const resp = ctrl.expectOne('http://localhost:3000/user/config')
+        resp.flush({ ngViewerState: mUserState})
+
+      })
+    })
+  })
+})
diff --git a/src/services/state/ngViewerState.store.ts b/src/services/state/ngViewerState.store.ts
index bc3b7fb8ce0abfcf2c3c1799a9fccf28238b71e3..b028a6be9f12ed69d22a7da64de7df40831c7dc9 100644
--- a/src/services/state/ngViewerState.store.ts
+++ b/src/services/state/ngViewerState.store.ts
@@ -1,11 +1,10 @@
 import { Injectable, OnDestroy } from '@angular/core';
-import { Observable, combineLatest, fromEvent, Subscription, from, of } from 'rxjs';
+import { Observable, combineLatest, fromEvent, Subscription, of } from 'rxjs';
 import { Effect, Actions, ofType } from '@ngrx/effects';
 import { withLatestFrom, map, distinctUntilChanged, scan, shareReplay, filter, mapTo, debounceTime, catchError, skip, throttleTime } from 'rxjs/operators';
-import { SNACKBAR_MESSAGE } from './uiState.store';
-import { getNgIds, IavRootStoreInterface, GENERAL_ACTION_TYPES } from '../stateStore.service';
+import { getNgIds } from 'src/util/fn';
 import { Action, select, Store, createReducer, on } from '@ngrx/store'
-import { BACKENDURL, CYCLE_PANEL_MESSAGE } from 'src/util/constants';
+import { CYCLE_PANEL_MESSAGE } from 'src/util/constants';
 import { HttpClient } from '@angular/common/http';
 import { INgLayerInterface, ngViewerActionAddNgLayer, ngViewerActionRemoveNgLayer, ngViewerActionSetPerspOctantRemoval } from './ngViewerState.store.helper'
 import { PureContantService } from 'src/util';
@@ -13,6 +12,8 @@ import { PANELS } from './ngViewerState.store.helper'
 import { ngViewerActionToggleMax, ngViewerActionClearView, ngViewerActionSetPanelOrder, ngViewerActionSwitchPanelMode, ngViewerActionForceShowSegment, ngViewerActionNehubaReady } from './ngViewerState/actions';
 import { generalApplyState } from '../stateStore.helper';
 import { ngViewerSelectorPanelMode, ngViewerSelectorPanelOrder } from './ngViewerState/selectors';
+import { uiActionSnackbarMessage } from './uiState/actions';
+import { TUserRouteError } from 'src/auth/auth.service';
 
 export function mixNgLayers(oldLayers: INgLayerInterface[], newLayers: INgLayerInterface|INgLayerInterface[]): INgLayerInterface[] {
   if (newLayers instanceof Array) {
@@ -145,6 +146,12 @@ export function stateStore(state, action) {
   return ngViewerStateReducer(state, action)
 }
 
+type TUserConfig = {
+
+}
+
+type TUserConfigResp = TUserConfig & TUserRouteError
+
 @Injectable({
   providedIn: 'root',
 })
@@ -181,7 +188,7 @@ export class NgViewerUseEffect implements OnDestroy {
 
   constructor(
     private actions: Actions,
-    private store$: Store<IavRootStoreInterface>,
+    private store$: Store<any>,
     private pureConstantService: PureContantService,
     private http: HttpClient,
   ){
@@ -201,7 +208,7 @@ export class NgViewerUseEffect implements OnDestroy {
         distinctUntilChanged(),
         throttleTime(1000)
       ).subscribe(ngViewerState => {
-        this.http.post(`${BACKENDURL}user/config`, JSON.stringify({ ngViewerState }),  {
+        this.http.post(`${this.pureConstantService.backendUrl}user/config`, JSON.stringify({ ngViewerState }),  {
           headers: {
             'Content-type': 'application/json'
           }
@@ -209,22 +216,26 @@ export class NgViewerUseEffect implements OnDestroy {
       })
     )
 
-    this.applySavedUserConfig$ = this.http.get(`${BACKENDURL}user/config`).pipe(
+    this.applySavedUserConfig$ = this.http.get<TUserConfigResp>(`${this.pureConstantService.backendUrl}user/config`).pipe(
+      map(json => {
+        if (json.error) {
+          throw new Error(json.message || 'User not loggedin.')
+        }
+        return json
+      }),
       catchError((err,caught) => of(null)),
       filter(v => !!v),
       withLatestFrom(this.store$),
-      map(([{ngViewerState: fetchedNgViewerState}, state]) => {
+      map(([{ ngViewerState: fetchedNgViewerState }, state]) => {
         const { ngViewerState } = state
-        return {
-          type: GENERAL_ACTION_TYPES.APPLY_STATE,
+        return generalApplyState({
           state: {
             ...state,
             ngViewerState: {
               ...ngViewerState,
               ...fetchedNgViewerState
-            }
-          }
-        }
+            }}
+        })
       })
     )
 
@@ -335,10 +346,9 @@ export class NgViewerUseEffect implements OnDestroy {
       filter(([_, useMobileUI]) => !useMobileUI),
       map(([toggleMaximiseMode, _]) => toggleMaximiseMode),
       filter(({ payload }) => payload.panelMode && payload.panelMode === PANELS.SINGLE_PANEL),
-      mapTo({
-        type: SNACKBAR_MESSAGE,
-        snackbarMessage: CYCLE_PANEL_MESSAGE,
-      }),
+      mapTo(uiActionSnackbarMessage({
+        snackbarMessage: CYCLE_PANEL_MESSAGE
+      })),
     )
 
     this.spacebarListener$ = fromEvent(document.body, 'keydown', { capture: true }).pipe(
diff --git a/src/services/state/ngViewerState/selectors.ts b/src/services/state/ngViewerState/selectors.ts
index db84dcdb944e8e9f3d07b80a8d02464a492d4c3b..7222296d4627a32cf20716d05e2ea9e81d898e2e 100644
--- a/src/services/state/ngViewerState/selectors.ts
+++ b/src/services/state/ngViewerState/selectors.ts
@@ -35,3 +35,8 @@ export const ngViewerSelectorNehubaReady = createSelector(
   state => state['ngViewerState'],
   ngViewerState => ngViewerState.nehubaReady
 )
+
+export const ngViewerSelectorLayers = createSelector(
+  state => state['ngViewerState'],
+  ngViewerState => ngViewerState?.layers || []
+)
\ No newline at end of file
diff --git a/src/services/state/pluginState.helper.ts b/src/services/state/pluginState.helper.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e1c48c1941ab716f1bacc6c8980f62c2367a933a
--- /dev/null
+++ b/src/services/state/pluginState.helper.ts
@@ -0,0 +1,15 @@
+import { createSelector } from "@ngrx/store"
+
+export const PLUGINSTORE_ACTION_TYPES = {
+  SET_INIT_PLUGIN: `SET_INIT_PLUGIN`,
+  CLEAR_INIT_PLUGIN: 'CLEAR_INIT_PLUGIN',
+}
+
+export const pluginStateSelectorInitManifests = createSelector(
+  state => state['pluginState'],
+  pluginState => pluginState.initManifests
+)
+
+export const PLUGINSTORE_CONSTANTS = {
+  INIT_MANIFEST_SRC: 'INIT_MANIFEST_SRC',
+}
diff --git a/src/services/state/pluginState.store.ts b/src/services/state/pluginState.store.ts
index de5b76754c0ea0af759923d62610ce30a3acf7ef..85bfa20a915ec8ab2e11497321cd66d95edabb91 100644
--- a/src/services/state/pluginState.store.ts
+++ b/src/services/state/pluginState.store.ts
@@ -1,6 +1,6 @@
 import { Action } from '@ngrx/store'
-import { GENERAL_ACTION_TYPES } from '../stateStore.service'
-
+import { generalApplyState } from '../stateStore.helper'
+import { PLUGINSTORE_ACTION_TYPES, PLUGINSTORE_CONSTANTS } from './pluginState.helper'
 export const defaultState: StateInterface = {
   initManifests: []
 }
@@ -16,14 +16,6 @@ export interface ActionInterface extends Action {
   }
 }
 
-export const PLUGINSTORE_ACTION_TYPES = {
-  SET_INIT_PLUGIN: `SET_INIT_PLUGIN`,
-  CLEAR_INIT_PLUGIN: 'CLEAR_INIT_PLUGIN',
-}
-
-export const PLUGINSTORE_CONSTANTS = {
-  INIT_MANIFEST_SRC: 'INIT_MANIFEST_SRC',
-}
 
 export const getStateStore = ({ state = defaultState } = {}) => (prevState: StateInterface = state, action: ActionInterface): StateInterface => {
   switch (action.type) {
@@ -45,7 +37,7 @@ export const getStateStore = ({ state = defaultState } = {}) => (prevState: Stat
       initManifests: newManifests,
     }
   }
-  case GENERAL_ACTION_TYPES.APPLY_STATE: {
+  case generalApplyState.type: {
     const { pluginState } = (action as any).state
     return pluginState
   }
diff --git a/src/services/state/uiState.store.helper.ts b/src/services/state/uiState.store.helper.ts
index 94e404cb5359acaf491e7b321e60a57d1a77e0a6..6f1c187c15b94170fcdc5db6b5ba3d0664d6da75 100644
--- a/src/services/state/uiState.store.helper.ts
+++ b/src/services/state/uiState.store.helper.ts
@@ -1,6 +1,6 @@
 // TODO merge with uiState.store.ts after refactor completes
 
-import {
+export {
   uiActionSetPreviewingDatasetFiles,
   uiActionShowSidePanelConnectivity,
   uiStateCloseSidePanel,
@@ -8,36 +8,17 @@ import {
   uiStateExpandSidePanel,
   uiStateOpenSidePanel,
   uiStateShowBottomSheet,
-  uiActionHideDatasetWithId,
-  uiActionShowDatasetWtihId,
+  uiActionSnackbarMessage,
+  uiActionMouseoverLandmark,
+  uiActionMouseoverSegments,
 } from './uiState/actions'
 
 export {
-  uiActionSetPreviewingDatasetFiles,
-  uiActionShowSidePanelConnectivity,
-  uiStateCloseSidePanel,
-  uiStateCollapseSidePanel,
-  uiStateExpandSidePanel,
-  uiStateOpenSidePanel,
-  uiStateShowBottomSheet,
-  uiActionHideDatasetWithId,
-  uiActionShowDatasetWtihId,
-}
-
-import {
   uiStatePreviewingDatasetFilesSelector,
   uiStateMouseOverSegmentsSelector,
   uiStateMouseoverUserLandmark,
-  uiStateShownDatasetIdSelector,
 } from './uiState/selectors'
 
-export {
-  uiStatePreviewingDatasetFilesSelector,
-  uiStateMouseOverSegmentsSelector,
-  uiStateMouseoverUserLandmark,
-  uiStateShownDatasetIdSelector,
-}
-
 export enum EnumWidgetTypes{
   DATASET_PREVIEW,
 }
@@ -52,3 +33,5 @@ export type TypeOpenedWidget = {
   type: EnumWidgetTypes
   data: IDatasetPreviewData
 }
+
+export const SHOW_KG_TOS = `SHOW_KG_TOS`
\ No newline at end of file
diff --git a/src/services/state/uiState.store.ts b/src/services/state/uiState.store.ts
index 5769426bfa2f5124e278d46fcdfbcc67af528650..cab51af1edb88fdbcf360fb5e5a3628e9809dc51 100644
--- a/src/services/state/uiState.store.ts
+++ b/src/services/state/uiState.store.ts
@@ -10,9 +10,8 @@ import { MatBottomSheetRef, MatBottomSheet } from '@angular/material/bottom-shee
 import { uiStateCloseSidePanel, uiStateOpenSidePanel, uiStateCollapseSidePanel, uiStateExpandSidePanel, uiActionSetPreviewingDatasetFiles, uiStateShowBottomSheet, uiActionShowSidePanelConnectivity } from './uiState.store.helper';
 import { viewerStateMouseOverCustomLandmark } from './viewerState/actions';
 import { IUiState } from './uiState/common'
-import { uiActionHideAllDatasets, uiActionHideDatasetWithId, uiActionShowDatasetWtihId } from './uiState/actions';
+import { uiActionMouseoverLandmark, uiActionMouseoverSegments, uiActionSnackbarMessage } from './uiState/actions';
 export const defaultState: IUiState = {
-  shownDatasetId: [],
 
   previewingDatasetFiles: [],
 
@@ -39,27 +38,7 @@ export { IUiState }
 
 export const getStateStore = ({ state = defaultState } = {}) => (prevState: IUiState = state, action: ActionInterface) => {
   switch (action.type) {
-  case uiActionHideDatasetWithId.type:{
-    return {
-      ...prevState,
-      shownDatasetId: prevState.shownDatasetId.filter(id => id !== (action as any).id)
-    }
-  }
-  case uiActionHideAllDatasets.type:{
-    return {
-      ...prevState,
-      shownDatasetId: []
-    }
-  }
-  case uiActionShowDatasetWtihId.type: {
-    return {
-      ...prevState,
-      shownDatasetId: prevState.shownDatasetId.concat(
-        (action as any).id
-      )
-    }
-  }
-  
+
   case uiActionSetPreviewingDatasetFiles.type: {
     const { previewingDatasetFiles } = action as any
     return {
@@ -67,7 +46,7 @@ export const getStateStore = ({ state = defaultState } = {}) => (prevState: IUiS
       previewingDatasetFiles
     }
   }
-  case MOUSE_OVER_SEGMENTS: {
+  case uiActionMouseoverSegments.type: {
     const { segments } = action
     return {
       ...prevState,
@@ -87,11 +66,12 @@ export const getStateStore = ({ state = defaultState } = {}) => (prevState: IUiS
       mouseOverUserLandmark,
     }
   }
-  case MOUSE_OVER_LANDMARK:
+  case uiActionMouseoverLandmark.type:
     return {
       ...prevState,
       mouseOverLandmark : action.landmark,
     }
+  case uiActionSnackbarMessage.type:
   case SNACKBAR_MESSAGE: {
     const { snackbarMessage } = action
     /**
@@ -258,8 +238,6 @@ export class UiStateUseEffect implements OnDestroy{
 }
 
 export const MOUSE_OVER_SEGMENT = `MOUSE_OVER_SEGMENT`
-export const MOUSE_OVER_SEGMENTS = `MOUSE_OVER_SEGMENTS`
-export const MOUSE_OVER_LANDMARK = `MOUSE_OVER_LANDMARK`
 
 export const CLOSE_SIDE_PANEL = `CLOSE_SIDE_PANEL`
 export const OPEN_SIDE_PANEL = `OPEN_SIDE_PANEL`
@@ -268,7 +246,6 @@ export const EXPAND_SIDE_PANEL_CURRENT_VIEW = `EXPAND_SIDE_PANEL_CURRENT_VIEW`
 
 export const AGREE_COOKIE = `AGREE_COOKIE`
 export const AGREE_KG_TOS = `AGREE_KG_TOS`
-export const SHOW_KG_TOS = `SHOW_KG_TOS`
 
-export const SNACKBAR_MESSAGE = `SNACKBAR_MESSAGE`
+export const SNACKBAR_MESSAGE = uiActionSnackbarMessage.type
 export const SHOW_BOTTOM_SHEET = `SHOW_BOTTOM_SHEET`
diff --git a/src/services/state/uiState/actions.ts b/src/services/state/uiState/actions.ts
index bfd0548766e1ce870b3747b155805657826316dc..7f3f864483c29e791532642da6351cfd42e0b395 100644
--- a/src/services/state/uiState/actions.ts
+++ b/src/services/state/uiState/actions.ts
@@ -24,6 +24,16 @@ export const uiStateShowBottomSheet = createAction(
   props<{ bottomSheetTemplate: TemplateRef<unknown>, config?: MatBottomSheetConfig }>()
 )
 
+export const uiActionMouseoverLandmark = createAction(
+  `[uiState] mouseoverLandmark`,
+  props<{ landmark: any }>()
+)
+
+export const uiActionMouseoverSegments = createAction(
+  `[uiState] mouseoverSegments`,
+  props<{ segments: any[] }>()
+)
+
 export const uiActionSetPreviewingDatasetFiles = createAction(
   `[uiState] setDatasetPreviews`,
   props<{previewingDatasetFiles: {datasetId: string, filename: string}[]}>()
@@ -33,16 +43,7 @@ export const uiActionShowSidePanelConnectivity = createAction(
   `[uiState] showSidePanelConnectivity`
 )
 
-export const uiActionShowDatasetWtihId = createAction(
-  `[uiState] showDatasetWithId`,
-  props<{ id: string }>()
-)
-
-export const uiActionHideDatasetWithId = createAction(
-  `[uiState] hideDatasetWithId`,
-  props<{ id: string }>()
-)
-
-export const uiActionHideAllDatasets = createAction(
-  `[uiState] hideAllDatasets`
-)
+export const uiActionSnackbarMessage = createAction(
+  `[uiState] snackbarMessage`,
+  props<{snackbarMessage: string}>()
+)
\ No newline at end of file
diff --git a/src/services/state/uiState/common.ts b/src/services/state/uiState/common.ts
index a5c833a42fc0c1a91fc2afeade927012e54f8d8b..7c3210c4df97b8399584f837dddf68db52db4d8e 100644
--- a/src/services/state/uiState/common.ts
+++ b/src/services/state/uiState/common.ts
@@ -1,5 +1,4 @@
 export interface IUiState{
-  shownDatasetId: string[]
 
   previewingDatasetFiles: {datasetId: string, filename: string}[]
 
diff --git a/src/services/state/uiState/selectors.ts b/src/services/state/uiState/selectors.ts
index 62064da340cce049eec2236c739bb7bd7eb15ab7..f72757b436adbf77e19becde70cff55213484504 100644
--- a/src/services/state/uiState/selectors.ts
+++ b/src/services/state/uiState/selectors.ts
@@ -26,8 +26,3 @@ export const uiStateMouseoverUserLandmark = createSelector(
   state => state['uiState'],
   uiState => uiState['mouseOverUserLandmark']
 )
-
-export const uiStateShownDatasetIdSelector = createSelector(
-  state => state['uiState'],
-  uiState => uiState['shownDatasetId']
-)
diff --git a/src/services/state/userConfigState.helper.spec.ts b/src/services/state/userConfigState.helper.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e007700e80d5b030b8d2d70cb41e71d64dfcd136
--- /dev/null
+++ b/src/services/state/userConfigState.helper.spec.ts
@@ -0,0 +1,47 @@
+import { selectorPluginCspPermission } from "./userConfigState.helper"
+
+describe('> userConfigState.helper.ts', () => {
+  describe('> selectorPluginCspPermission', () => {
+    const expectedTrue = {
+      value: true
+    }
+    const expectedFalse = {
+      value: false
+    }
+    describe('> malformed init value', () => {
+      describe('> undefined userconfigstate', () => {
+        it('> return expected false val', () => {
+          const returnVal = selectorPluginCspPermission.projector(null, { key: 'foo-bar' })
+          expect(returnVal).toEqual(expectedFalse)
+        })
+      })
+      describe('> undefined pluginCsp property', () => {
+        it('> return expected false val', () => {
+          const returnVal = selectorPluginCspPermission.projector({}, { key: 'foo-bar' })
+          expect(returnVal).toEqual(expectedFalse)
+        })
+      })
+    })
+
+    describe('> well fored init valu', () => {
+
+      describe('> undefined key', () => {
+        it('> return expected false val', () => {
+          const returnVal = selectorPluginCspPermission.projector({
+            pluginCsp: {'yes-man': true}
+          }, { key: 'foo-bar' })
+          expect(returnVal).toEqual(expectedFalse)
+        })
+      })
+
+      describe('> truthly defined key', () => {
+        it('> return expected true val', () => {
+          const returnVal = selectorPluginCspPermission.projector({ pluginCsp:
+            { 'foo-bar': true }
+          }, { key: 'foo-bar' })
+          expect(returnVal).toEqual(expectedTrue)
+        })
+      })
+    })
+  })
+})
\ No newline at end of file
diff --git a/src/services/state/userConfigState.helper.ts b/src/services/state/userConfigState.helper.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e71be024c1947ccf0fc3f3aca909cebbceeaf046
--- /dev/null
+++ b/src/services/state/userConfigState.helper.ts
@@ -0,0 +1,11 @@
+import { createSelector } from "@ngrx/store"
+
+export const selectorPluginCspPermission = createSelector(
+  (state: any) => state.userConfigState,
+  (userConfigState: any, props: any = {}) => {
+    const { key } = props as { key: string }
+    return {
+      value: !!userConfigState?.pluginCsp?.[key]
+    } 
+  }
+)
diff --git a/src/services/state/userConfigState.store.spec.ts b/src/services/state/userConfigState.store.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6b497bec1c9e9e9a0a536021532c2add3aa14be2
--- /dev/null
+++ b/src/services/state/userConfigState.store.spec.ts
@@ -0,0 +1,88 @@
+import { HttpClientTestingModule, HttpTestingController } from "@angular/common/http/testing"
+import { fakeAsync, TestBed, tick } from "@angular/core/testing"
+import { provideMockActions } from "@ngrx/effects/testing"
+import { Action } from "@ngrx/store"
+import { MockStore, provideMockStore } from "@ngrx/store/testing"
+import { from, Observable } from "rxjs"
+import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module"
+import { PureContantService } from "src/util"
+import { DialogService } from "../dialogService.service"
+import { actionUpdatePluginCsp, UserConfigStateUseEffect } from "./userConfigState.store"
+import { viewerStateSelectedParcellationSelector, viewerStateSelectedRegionsSelector, viewerStateSelectedTemplateSelector } from "./viewerState/selectors"
+
+describe('> userConfigState.store.spec.ts', () => {
+  describe('> UserConfigStateUseEffect', () => {
+    let action$: Observable<Action>
+    beforeEach(() => {
+      TestBed.configureTestingModule({
+        imports: [
+          HttpClientTestingModule,
+          AngularMaterialModule,
+        ],
+        providers: [
+          provideMockActions(() => action$),
+          provideMockStore({
+            initialState: {
+              viewerConfigState: {
+                gpuLimit: 1e9,
+                animation: true
+              }
+            }
+          }),
+          DialogService,
+          {
+            provide: PureContantService,
+            useValue: {
+              backendUrl: 'http://localhost:3000/'
+            }
+          }
+        ]
+      })
+
+      const mockStore = TestBed.inject(MockStore)
+      mockStore.overrideSelector(viewerStateSelectedTemplateSelector, null)
+      mockStore.overrideSelector(viewerStateSelectedParcellationSelector, null)
+      mockStore.overrideSelector(viewerStateSelectedRegionsSelector, [])
+    })
+
+    it('> can be init', () => {
+      const useEffect = TestBed.inject(UserConfigStateUseEffect)
+      expect(useEffect).toBeTruthy()
+    })
+    describe('> setInitPluginPermission$', () => {
+      let mockHttp: HttpTestingController
+      let useEffect: UserConfigStateUseEffect
+      const mockpluginPer = {
+        'foo-bar': {
+          'script-src': [
+            '1',
+            '2',
+          ]
+        }
+      }
+      beforeEach(() => {
+        mockHttp = TestBed.inject(HttpTestingController)
+        useEffect = TestBed.inject(UserConfigStateUseEffect)
+      })
+      afterEach(() => {
+        mockHttp.verify()
+      })
+      it('> calls /GET user/pluginPermissions', fakeAsync(() => {
+        let val
+        useEffect.setInitPluginPermission$.subscribe(v => val = v)
+        tick(20)
+        const req = mockHttp.expectOne(`http://localhost:3000/user/pluginPermissions`)
+        req.flush(mockpluginPer)
+        expect(val).toEqual(actionUpdatePluginCsp({ payload: mockpluginPer }))
+      }))
+
+      it('> if get fn fails', fakeAsync(() => {
+        let val
+        useEffect.setInitPluginPermission$.subscribe(v => val = v)
+        const req = mockHttp.expectOne(`http://localhost:3000/user/pluginPermissions`)
+        req.error(null, { status: 500, statusText: 'Internal Error' })
+        expect(val).toEqual(actionUpdatePluginCsp({ payload: {} }))
+      }))
+    })
+  })
+})
\ No newline at end of file
diff --git a/src/services/state/userConfigState.store.ts b/src/services/state/userConfigState.store.ts
index db913779d0148b5f7bbc8bddbc09dfe5431de1cd..81fef3205c4020651d6169012d43c19da67ec0f9 100644
--- a/src/services/state/userConfigState.store.ts
+++ b/src/services/state/userConfigState.store.ts
@@ -1,21 +1,36 @@
 import { Injectable, OnDestroy } from "@angular/core";
 import { Actions, Effect, ofType } from "@ngrx/effects";
-import { Action, select, Store } from "@ngrx/store";
+import { Action, createAction, createReducer, props, select, Store, on, createSelector } from "@ngrx/store";
 import { combineLatest, from, Observable, of, Subscription } from "rxjs";
-import { catchError, distinctUntilChanged, filter, map, share, shareReplay, switchMap, take, withLatestFrom } from "rxjs/operators";
-import { LOCAL_STORAGE_CONST } from "src/util//constants";
+import { catchError, distinctUntilChanged, filter, map, mapTo, share, shareReplay, switchMap, take, withLatestFrom } from "rxjs/operators";
+import { BACKENDURL, LOCAL_STORAGE_CONST } from "src/util//constants";
 import { DialogService } from "../dialogService.service";
-import { generateLabelIndexId, IavRootStoreInterface, recursiveFindRegionWithLabelIndexId } from "../stateStore.service";
-import { NEWVIEWER, SELECT_PARCELLATION, SELECT_REGIONS } from "./viewerState.store";
-
+import { recursiveFindRegionWithLabelIndexId } from "src/util/fn";
+import { serialiseParcellationRegion } from 'common/util'
 // Get around the problem of importing duplicated string (ACTION_TYPES), even using ES6 alias seems to trip up the compiler
 // TODO file bug and reverse
-import * as viewerConfigStore from './viewerConfig.store'
-
-const SET_MOBILE_UI = viewerConfigStore.VIEWER_CONFIG_ACTION_TYPES.SET_MOBILE_UI
+import { HttpClient } from "@angular/common/http";
+import { actionSetMobileUi, viewerStateNewViewer, viewerStateSelectParcellation, viewerStateSetSelectedRegions } from "./viewerState/actions";
+import { viewerStateSelectedParcellationSelector, viewerStateSelectedRegionsSelector, viewerStateSelectedTemplateSelector } from "./viewerState/selectors";
+import { PureContantService } from "src/util";
+
+interface ICsp{
+  'connect-src'?: string[]
+  'script-src'?: string[]
+}
 
 export interface StateInterface {
   savedRegionsSelection: RegionSelection[]
+  /**
+   * plugin csp - currently store in localStorage
+   * if user log in, store in user profile
+   */
+  pluginCsp: {
+    /**
+     * key === plugin version id 
+     */
+    [key: string]: ICsp
+  }
 }
 
 export interface RegionSelection {
@@ -43,11 +58,31 @@ interface UserConfigAction extends Action {
 }
 
 export const defaultState: StateInterface = {
-  savedRegionsSelection: []
+  savedRegionsSelection: [],
+  pluginCsp: {}
 }
 
+export const actionUpdateRegionSelections = createAction(
+  `[userConfig] updateRegionSelections`,
+  props<{ config: { savedRegionsSelection: RegionSelection[]} }>()
+)
+
+export const selectorAllPluginsCspPermission = createSelector(
+  (state: any) => state.userConfigState,
+  userConfigState => userConfigState.pluginCsp
+)
+
+export const actionUpdatePluginCsp = createAction(
+  `[userConfig] updatePluginCspPermission`,
+  props<{
+    payload: {
+      [key: string]: ICsp
+    }
+  }>()
+)
+
 export const ACTION_TYPES = {
-  UPDATE_REGIONS_SELECTIONS: `UPDATE_REGIONS_SELECTIONS`,
+  UPDATE_REGIONS_SELECTIONS: actionUpdateRegionSelections.type,
   UPDATE_REGIONS_SELECTION: 'UPDATE_REGIONS_SELECTION',
   SAVE_REGIONS_SELECTION: `SAVE_REGIONS_SELECTIONN`,
   DELETE_REGIONS_SELECTION: 'DELETE_REGIONS_SELECTION',
@@ -55,32 +90,23 @@ export const ACTION_TYPES = {
   LOAD_REGIONS_SELECTION: 'LOAD_REGIONS_SELECTION',
 }
 
-export const getStateStore = ({ state = defaultState } = {}) => (prevState: StateInterface = state, action: UserConfigAction) => {
-  switch (action.type) {
-  case ACTION_TYPES.UPDATE_REGIONS_SELECTIONS: {
-    const { config = {} } = action
+
+export const userConfigReducer = createReducer(
+  defaultState,
+  on(actionUpdateRegionSelections, (state, { config }) => {
     const { savedRegionsSelection } = config
     return {
-      ...prevState,
-      savedRegionsSelection,
+      ...state,
+      savedRegionsSelection
     }
-  }
-  default: return prevState
-  }
-}
-
-// must export a named function for aot compilation
-// see https://github.com/angular/angular/issues/15587
-// https://github.com/amcdnl/ngrx-actions/issues/23
-// or just google for:
-//
-// angular function expressions are not supported in decorators
-
-const defaultStateStore = getStateStore()
-
-export function stateStore(state, action) {
-  return defaultStateStore(state, action)
-}
+  }),
+  on(actionUpdatePluginCsp, (state, { payload }) => {
+    return {
+      ...state,
+      pluginCsp: payload
+    }
+  })
+)
 
 @Injectable({
   providedIn: 'root',
@@ -91,28 +117,29 @@ export class UserConfigStateUseEffect implements OnDestroy {
 
   constructor(
     private actions$: Actions,
-    private store$: Store<IavRootStoreInterface>,
+    private store$: Store<any>,
     private dialogService: DialogService,
+    private http: HttpClient,
+    private constantSvc: PureContantService,
   ) {
     const viewerState$ = this.store$.pipe(
       select('viewerState'),
       shareReplay(1),
     )
 
-    this.parcellationSelected$ = viewerState$.pipe(
-      select('parcellationSelected'),
+    this.parcellationSelected$ = this.store$.pipe(
+      select(viewerStateSelectedParcellationSelector),
       distinctUntilChanged(),
-      share(),
     )
 
     this.tprSelected$ = combineLatest(
-      viewerState$.pipe(
-        select('templateSelected'),
+      this.store$.pipe(
+        select(viewerStateSelectedTemplateSelector),
         distinctUntilChanged(),
       ),
       this.parcellationSelected$,
-      viewerState$.pipe(
-        select('regionsSelected'),
+      this.store$.pipe(
+        select(viewerStateSelectedRegionsSelector)
         /**
          * TODO
          * distinct selectedRegions
@@ -124,7 +151,6 @@ export class UserConfigStateUseEffect implements OnDestroy {
           templateSelected, parcellationSelected, regionsSelected,
         }
       }),
-      shareReplay(1),
     )
 
     this.savedRegionsSelections$ = this.store$.pipe(
@@ -222,13 +248,14 @@ export class UserConfigStateUseEffect implements OnDestroy {
         switchMap(({ savedRegionsSelection, parcellationSelected, templateSelected, regionsSelected }) => {
           if (templateSelected.name !== savedRegionsSelection.templateSelected.name ) {
             /**
-             * template different, dispatch NEWVIEWER
+             * template different, dispatch viewerStateNewViewer.type
              */
-            this.store$.dispatch({
-              type: NEWVIEWER,
-              selectParcellation: savedRegionsSelection.parcellationSelected,
-              selectTemplate: savedRegionsSelection.templateSelected,
-            })
+            this.store$.dispatch(
+              viewerStateNewViewer({
+                selectParcellation: savedRegionsSelection.parcellationSelected,
+                selectTemplate: savedRegionsSelection.templateSelected,
+              })
+            )
             return this.parcellationSelected$.pipe(
               filter(p => p.updated),
               take(1),
@@ -244,11 +271,11 @@ export class UserConfigStateUseEffect implements OnDestroy {
             /**
              * parcellation different, dispatch SELECT_PARCELLATION
              */
-
-            this.store$.dispatch({
-              type: SELECT_PARCELLATION,
-              selectParcellation: savedRegionsSelection.parcellationSelected,
-            })
+            this.store$.dispatch(
+              viewerStateSelectParcellation({
+                selectParcellation: savedRegionsSelection.parcellationSelected,
+              })
+            )
             return this.parcellationSelected$.pipe(
               filter(p => p.updated),
               take(1),
@@ -265,10 +292,11 @@ export class UserConfigStateUseEffect implements OnDestroy {
           })
         }),
       ).subscribe(({ regionsSelected }) => {
-        this.store$.dispatch({
-          type: SELECT_REGIONS,
-          selectRegions: regionsSelected,
-        })
+        this.store$.dispatch(
+          viewerStateSetSelectedRegions({
+            selectRegions: regionsSelected,
+          })
+        )
       }),
     )
 
@@ -288,8 +316,7 @@ export class UserConfigStateUseEffect implements OnDestroy {
 
     this.subscriptions.push(
       this.actions$.pipe(
-
-        ofType(SET_MOBILE_UI),
+        ofType(actionSetMobileUi.type),
         map((action: any) => {
           const { payload } = action
           const { useMobileUI } = payload
@@ -313,7 +340,7 @@ export class UserConfigStateUseEffect implements OnDestroy {
             name,
             tName: templateSelected.name,
             pName: parcellationSelected.name,
-            rSelected: regionsSelected.map(({ ngId, labelIndex }) => generateLabelIndexId({ ngId, labelIndex })),
+            rSelected: regionsSelected.map(({ ngId, labelIndex }) => serialiseParcellationRegion({ ngId, labelIndex })),
           } as SimpleRegionSelection
         })
 
@@ -334,7 +361,11 @@ export class UserConfigStateUseEffect implements OnDestroy {
       map(fetchedTemplates => savedSRSs.map(({ id, name, tName, pName, rSelected }) => {
         const templateSelected = fetchedTemplates.find(t => t.name === tName)
         const parcellationSelected = templateSelected && templateSelected.parcellations.find(p => p.name === pName)
-        const regionsSelected = parcellationSelected && rSelected.map(labelIndexId => recursiveFindRegionWithLabelIndexId({ regions: parcellationSelected.regions, labelIndexId, inheritedNgId: parcellationSelected.ngId }))
+        const regionsSelected = parcellationSelected && rSelected.map(labelIndexId => recursiveFindRegionWithLabelIndexId({
+          regions: parcellationSelected.regions,
+          labelIndexId,
+          inheritedNgId: parcellationSelected.ngId
+        }))
         return {
           templateSelected,
           parcellationSelected,
@@ -378,4 +409,15 @@ export class UserConfigStateUseEffect implements OnDestroy {
 
   @Effect()
   public restoreSRSsFromStorage$: Observable<any>
+
+  @Effect()
+  public setInitPluginPermission$ = this.http.get(`${this.constantSvc.backendUrl}user/pluginPermissions`, {
+    responseType: 'json'
+  }).pipe(
+    /**
+     * TODO show warning?
+     */
+    catchError(() => of({})),
+    map((json: any) => actionUpdatePluginCsp({ payload: json }))
+  )
 }
diff --git a/src/services/state/viewerConfig.store.ts b/src/services/state/viewerConfig.store.ts
index 74d2a657288cab2f4469a5eb8eb6c6f85dddf47b..a80981b30db8fc07b5bb3c33a77534c635264ccd 100644
--- a/src/services/state/viewerConfig.store.ts
+++ b/src/services/state/viewerConfig.store.ts
@@ -2,6 +2,7 @@ import { Action } from "@ngrx/store";
 import { LOCAL_STORAGE_CONST } from "src/util/constants";
 
 import { IViewerConfigState as StateInterface } from './viewerConfig.store.helper'
+import { actionSetMobileUi } from "./viewerState/actions";
 export { StateInterface }
 
 interface ViewerConfigurationAction extends Action {
@@ -22,8 +23,7 @@ export const CONFIG_CONSTANTS = {
 export const VIEWER_CONFIG_ACTION_TYPES = {
   SET_ANIMATION: `SET_ANIMATION`,
   UPDATE_CONFIG: `UPDATE_CONFIG`,
-  CHANGE_GPU_LIMIT: `CHANGE_GPU_LIMIT`,
-  SET_MOBILE_UI: 'SET_MOBILE_UI',
+  SET_MOBILE_UI: actionSetMobileUi.type,
 }
 
 // get gpu limit
@@ -73,18 +73,6 @@ export const getStateStore = ({ state = defaultState } = {}) => (prevState: Stat
       ...prevState,
       ...action.config,
     }
-  case VIEWER_CONFIG_ACTION_TYPES.CHANGE_GPU_LIMIT: {
-    const newGpuLimit = Math.min(
-      CONFIG_CONSTANTS.gpuLimitMax,
-      Math.max(
-        (prevState.gpuLimit || CONFIG_CONSTANTS.defaultGpuLimit) + action.payload.delta,
-        CONFIG_CONSTANTS.gpuLimitMin,
-      ))
-    return {
-      ...prevState,
-      gpuLimit: newGpuLimit,
-    }
-  }
   default: return prevState
   }
 }
diff --git a/src/services/state/viewerConfig/selectors.ts b/src/services/state/viewerConfig/selectors.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4b69c0e58e84b67d30b509f09c8f6df9ff67b787
--- /dev/null
+++ b/src/services/state/viewerConfig/selectors.ts
@@ -0,0 +1,6 @@
+import { createSelector } from "@ngrx/store";
+
+export const selectViewerConfigAnimationFlag = createSelector(
+  state => state['viewerConfigState'],
+  viewerConfigState => viewerConfigState['animation']
+)
diff --git a/src/services/state/viewerState.store.helper.spec.ts b/src/services/state/viewerState.store.helper.spec.ts
index c31955d5498e5a2b37f1e78fb26c84a3337b0ec3..fa0845554d221ea0051658ba8d096145913be883 100644
--- a/src/services/state/viewerState.store.helper.spec.ts
+++ b/src/services/state/viewerState.store.helper.spec.ts
@@ -1,6 +1,252 @@
-import { isNewerThan } from "./viewerState.store.helper"
+import { TestBed } from "@angular/core/testing"
+import { Action } from "@ngrx/store"
+import { provideMockActions } from "@ngrx/effects/testing"
+import { MockStore, provideMockStore } from "@ngrx/store/testing"
+import { Observable, of } from "rxjs"
+import { isNewerThan, ViewerStateHelperEffect } from "./viewerState.store.helper"
+import { viewerStateGetSelectedAtlas, viewerStateSelectedTemplateSelector } from "./viewerState/selectors"
+import { viewerStateHelperSelectParcellationWithId, viewerStateRemoveAdditionalLayer } from "./viewerState/actions"
+import { generalActionError } from "../stateStore.helper"
+import { hot } from "jasmine-marbles"
 
 describe('> viewerState.store.helper.ts', () => {
+  const tmplId = 'test-tmpl-id'
+  const tmplId0 = 'test-tmpl-id-0'
+  describe('> ViewerStateHelperEffect', () => {
+    let effect: ViewerStateHelperEffect
+    let mockStore: MockStore
+    let actions$: Observable<Action>
+    beforeEach(() => {
+      TestBed.configureTestingModule({
+        providers: [
+          ViewerStateHelperEffect,
+          provideMockStore(),
+          provideMockActions(() => actions$)
+        ]
+      })
+
+      mockStore = TestBed.inject(MockStore)
+      mockStore.overrideSelector(viewerStateSelectedTemplateSelector, {
+        ['@id']: tmplId
+      })
+
+      actions$ = of(
+        viewerStateRemoveAdditionalLayer({
+          payload: {
+            ['@id']: 'bla'
+          }
+        })
+      )
+    })
+
+    describe('> if selected atlas has no matching tmpl space', () => {
+      beforeEach(() => {
+        mockStore.overrideSelector(viewerStateGetSelectedAtlas, {
+          templateSpaces: [{
+            ['@id']: tmplId0
+          }]
+        })
+      })
+      it('> should emit gernal error', () => {
+        effect = TestBed.inject(ViewerStateHelperEffect)
+        effect.onRemoveAdditionalLayer$.subscribe(val => {
+          expect(val.type === generalActionError.type)
+        })
+      })
+    })
+
+    describe('> if selected atlas has matching tmpl', () => {
+
+      const parcId0 = 'test-parc-id-0'
+      const parcId1 = 'test-parc-id-1'
+      const tmpSp = {
+        ['@id']: tmplId,
+        availableIn: [{
+          ['@id']: parcId0
+        }],
+      }
+      beforeEach(() => {
+        mockStore.overrideSelector(viewerStateGetSelectedAtlas, {
+          templateSpaces: [
+            tmpSp
+          ],
+          parcellations: [],
+        })
+      })
+
+      describe('> if parc is empty array', () => {
+        it('> should emit with falsy as payload', () => {
+          effect = TestBed.inject(ViewerStateHelperEffect)
+          expect(
+            effect.onRemoveAdditionalLayer$
+          ).toBeObservable(
+            hot('(a|)', {
+              a: viewerStateHelperSelectParcellationWithId({
+                payload: undefined
+              })
+            })
+          )
+        })
+      })
+      describe('> if no parc has eligible @id', () => {
+        beforeEach(() => {
+          mockStore.overrideSelector(viewerStateGetSelectedAtlas, {
+            templateSpaces: [
+              tmpSp
+            ],
+            parcellations: [{
+              ['@id']: parcId1
+            }]
+          })
+        })
+        it('> should emit with falsy as payload', () => {
+          effect = TestBed.inject(ViewerStateHelperEffect)
+          expect(
+            effect.onRemoveAdditionalLayer$
+          ).toBeObservable(
+            hot('(a|)', {
+              a: viewerStateHelperSelectParcellationWithId({
+                payload: undefined
+              })
+            })
+          )
+        })
+      })
+
+      describe('> if some parc has eligible @id', () => {
+        describe('> if no @version is available', () => {
+          const parc1 = {
+            ['@id']: parcId0,
+            name: 'p0-0',
+            baseLayer: true
+          }
+          const parc2 = {
+            ['@id']: parcId0,
+            name: 'p0-1',
+            baseLayer: true
+          }
+          beforeEach(() => {
+
+            mockStore.overrideSelector(viewerStateGetSelectedAtlas, {
+              templateSpaces: [
+                tmpSp
+              ],
+              parcellations: [
+                parc1,
+                parc2
+              ]
+            })
+          })
+          it('> selects the first parc', () => {
+
+            effect = TestBed.inject(ViewerStateHelperEffect)
+            expect(
+              effect.onRemoveAdditionalLayer$
+            ).toBeObservable(
+              hot('(a|)', {
+                a: viewerStateHelperSelectParcellationWithId({
+                  payload: parc1
+                })
+              })
+            )
+          })
+        })
+
+        describe('> if @version is available', () => {
+          
+          describe('> if there exist an entry without @next attribute', () => {
+            
+            const parc1 = {
+              ['@id']: parcId0,
+              name: 'p0-0',
+              baseLayer: true,
+              ['@version']: {
+                ['@next']: 'random-value'
+              }
+            }
+            const parc2 = {
+              ['@id']: parcId0,
+              name: 'p0-1',
+              baseLayer: true,
+              ['@version']: {
+                ['@next']: null
+              }
+            }
+            beforeEach(() => {
+
+              mockStore.overrideSelector(viewerStateGetSelectedAtlas, {
+                templateSpaces: [
+                  tmpSp
+                ],
+                parcellations: [
+                  parc1,
+                  parc2
+                ]
+              })
+            })
+            it('> selects the first one without @next attribute', () => {
+
+              effect = TestBed.inject(ViewerStateHelperEffect)
+              expect(
+                effect.onRemoveAdditionalLayer$
+              ).toBeObservable(
+                hot('(a|)', {
+                  a: viewerStateHelperSelectParcellationWithId({
+                    payload: parc2
+                  })
+                })
+              )
+            })
+          })
+          describe('> if there exist no entry without @next attribute', () => {
+            
+            const parc1 = {
+              ['@id']: parcId0,
+              name: 'p0-0',
+              baseLayer: true,
+              ['@version']: {
+                ['@next']: 'random-value'
+              }
+            }
+            const parc2 = {
+              ['@id']: parcId0,
+              name: 'p0-1',
+              baseLayer: true,
+              ['@version']: {
+                ['@next']: 'another-random-value'
+              }
+            }
+            beforeEach(() => {
+
+              mockStore.overrideSelector(viewerStateGetSelectedAtlas, {
+                templateSpaces: [
+                  tmpSp
+                ],
+                parcellations: [
+                  parc1,
+                  parc2
+                ]
+              })
+            })
+            it('> selects the first one without @next attribute', () => {
+
+              effect = TestBed.inject(ViewerStateHelperEffect)
+              expect(
+                effect.onRemoveAdditionalLayer$
+              ).toBeObservable(
+                hot('(a|)', {
+                  a: viewerStateHelperSelectParcellationWithId({
+                    payload: parc1
+                  })
+                })
+              )
+            })
+          })
+        })
+      })
+    })
+  })
+
   describe('> isNewerThan', () => {
     describe('> ill formed versions', () => {
       it('> in circular references, throws', () => {
diff --git a/src/services/state/viewerState.store.helper.ts b/src/services/state/viewerState.store.helper.ts
index 8bfe2d676df861a97eb62a58259b877f4a6bd4d9..bf5f69fb89b4e9dbed57fd4066927a0254758944 100644
--- a/src/services/state/viewerState.store.helper.ts
+++ b/src/services/state/viewerState.store.helper.ts
@@ -15,12 +15,14 @@ import {
   viewerStateSelectParcellation,
   viewerStateSelectTemplateWithId,
   viewerStateSetConnectivityRegion,
+  viewerStateNehubaLayerchanged,
   viewerStateSetFetchedAtlases,
   viewerStateSetSelectedRegions,
   viewerStateSetSelectedRegionsWithIds,
   viewerStateToggleLayer,
   viewerStateToggleRegionSelect,
   viewerStateSelectRegionWithIdDeprecated,
+  viewerStateSetViewerMode,
   viewerStateDblClickOnViewer,
   viewerStateAddUserLandmarks,
   viewreStateRemoveUserLandmarks,
@@ -38,12 +40,14 @@ export {
   viewerStateSelectParcellation,
   viewerStateSelectTemplateWithId,
   viewerStateSetConnectivityRegion,
+  viewerStateNehubaLayerchanged,
   viewerStateSetFetchedAtlases,
   viewerStateSetSelectedRegions,
   viewerStateSetSelectedRegionsWithIds,
   viewerStateToggleLayer,
   viewerStateToggleRegionSelect,
   viewerStateSelectRegionWithIdDeprecated,
+  viewerStateSetViewerMode,
   viewerStateDblClickOnViewer,
   viewerStateAddUserLandmarks,
   viewreStateRemoveUserLandmarks,
@@ -110,7 +114,7 @@ export const viewerStateMetaReducers = [
 
 export class ViewerStateHelperEffect{
   @Effect()
-  selectParcellationWithId$: Observable<any> = this.actions$.pipe(
+  onRemoveAdditionalLayer$: Observable<any> = this.actions$.pipe(
     ofType(viewerStateRemoveAdditionalLayer.type),
     withLatestFrom(
       this.store$.pipe(
@@ -131,7 +135,8 @@ export class ViewerStateHelperEffect{
       const eligibleParcIdSet = new Set(
         tmpl.availableIn.map(p => p['@id'])
       )
-      const baseLayer = selectedAtlas['parcellations'].find(fullP => fullP['baseLayer'] && eligibleParcIdSet.has(fullP['@id']))
+      const baseLayers = selectedAtlas['parcellations'].filter(fullP => fullP['baseLayer'] && eligibleParcIdSet.has(fullP['@id']))
+      const baseLayer = baseLayers.find(layer => !!layer['@version'] && !layer['@version']['@next']) || baseLayers[0]
       return viewerStateHelperSelectParcellationWithId({ payload: baseLayer })
     })
   )
@@ -140,7 +145,7 @@ export class ViewerStateHelperEffect{
     private store$: Store<any>,
     private actions$: Actions
   ){
-    
+
   }
 }
 
@@ -177,7 +182,7 @@ export function isNewerThan(arr: IHasVersion[], srcObj: IHasId, compObj: IHasId)
     while (currPreviousId) {
       it += 1
       if (it>100) throw new Error(`iteration excced 100, did you include a loop?`)
-      
+
       const curr = arr.find(v => v['@version']['@this'] === currPreviousId)
       if (!curr) throw new Error(`GenNewerVersions error, version id ${currPreviousId} not found`)
       currPreviousId = curr['@version'][ flag ? '@next' : '@previous' ]
@@ -193,8 +198,8 @@ export function isNewerThan(arr: IHasVersion[], srcObj: IHasId, compObj: IHasId)
   for (const obj of GenNewerVersions(false)) {
     if (obj['@version']['@this'] === compObj['@id']) {
       return true
-    } 
+    }
   }
-  
+
   throw new Error(`isNewerThan error, neither srcObj nor compObj exist in array`)
 }
diff --git a/src/services/state/viewerState.store.ts b/src/services/state/viewerState.store.ts
index 25adedb5c6eb7fecba4215550f1376b9379c4d72..ed2422c70f8c2746d8e6d79fb903cb866f126452 100644
--- a/src/services/state/viewerState.store.ts
+++ b/src/services/state/viewerState.store.ts
@@ -7,13 +7,12 @@ import { IUserLandmark } from 'src/atlasViewer/atlasViewer.apiService.service';
 import { INgLayerInterface } from 'src/atlasViewer/atlasViewer.component';
 import { getViewer } from 'src/util/fn';
 import { LoggingService } from 'src/logging';
-import { generateLabelIndexId, IavRootStoreInterface } from '../stateStore.service';
+import { IavRootStoreInterface } from '../stateStore.service';
 import { GENERAL_ACTION_TYPES } from '../stateStore.service'
 import { CLOSE_SIDE_PANEL } from './uiState.store';
-import { 
+import {
   viewerStateSetSelectedRegions,
   viewerStateSetConnectivityRegion,
-  viewerStateSelectAtlas,
   viewerStateSelectParcellation,
   viewerStateSelectRegionWithIdDeprecated,
   viewerStateCustomLandmarkSelector,
@@ -24,8 +23,14 @@ import {
   viewerStateMouseOverCustomLandmarkInPerspectiveView,
   viewerStateNewViewer
 } from './viewerState.store.helper';
-import { cvtNehubaConfigToNavigationObj } from 'src/ui/viewerStateController/viewerState.useEffect';
-import { viewerStateChangeNavigation } from './viewerState/actions';
+import { cvtNehubaConfigToNavigationObj } from 'src/state';
+import {
+  viewerStateChangeNavigation,
+  viewerStateNehubaLayerchanged,
+  viewerStateSetViewerMode,
+  actionSelectLandmarks
+} from './viewerState/actions';
+import { serialiseParcellationRegion } from "common/util"
 
 export interface StateInterface {
   fetchedTemplates: any[]
@@ -34,6 +39,8 @@ export interface StateInterface {
   parcellationSelected: any | null
   regionsSelected: any[]
 
+  viewerMode: string
+
   landmarksSelected: any[]
   userLandmarks: IUserLandmark[]
 
@@ -75,6 +82,7 @@ export const defaultState: StateInterface = {
   fetchedTemplates : [],
   loadedNgLayers: [],
   regionsSelected: [],
+  viewerMode: null,
   userLandmarks: [],
   dedicatedView: null,
   navigation: null,
@@ -111,7 +119,7 @@ export const getStateStore = ({ state = defaultState } = {}) => (prevState: Part
       ...prevState,
       standaloneVolumes: []
     }
-  case NEWVIEWER: {
+  case viewerStateNewViewer.type: {
 
     const {
       selectParcellation: parcellation,
@@ -163,13 +171,19 @@ export const getStateStore = ({ state = defaultState } = {}) => (prevState: Part
       regionsSelected: selectRegions,
     }
   }
+  case viewerStateSetViewerMode.type: {
+    return {
+      ...prevState,
+      viewerMode: action.payload
+    }
+  }
   case DESELECT_LANDMARKS : {
     return {
       ...prevState,
       landmarksSelected : prevState.landmarksSelected.filter(lm => action.deselectLandmarks.findIndex(dLm => dLm.name === lm.name) < 0),
     }
   }
-  case SELECT_LANDMARKS : {
+  case actionSelectLandmarks.type: {
     return {
       ...prevState,
       landmarksSelected : action.landmarks,
@@ -185,7 +199,7 @@ export const getStateStore = ({ state = defaultState } = {}) => (prevState: Part
      * TODO
      * duplicated with ngViewerState.layers ?
      */
-  case NEHUBA_LAYER_CHANGED: {
+  case viewerStateNehubaLayerchanged.type: {
     const viewer = getViewer()
     if (!viewer) {
       return {
@@ -223,7 +237,7 @@ export const getStateStore = ({ state = defaultState } = {}) => (prevState: Part
     return {
       ...prevState,
       overwrittenColorMap: action.payload || '',
-    }  
+    }
   default :
     return prevState
   }
@@ -245,23 +259,19 @@ export function stateStore(state, action) {
 export const LOAD_DEDICATED_LAYER = 'LOAD_DEDICATED_LAYER'
 export const UNLOAD_DEDICATED_LAYER = 'UNLOAD_DEDICATED_LAYER'
 
-export const NEWVIEWER = viewerStateNewViewer.type
-
 export const FETCHED_TEMPLATE = 'FETCHED_TEMPLATE'
 export const CHANGE_NAVIGATION = viewerStateChangeNavigation.type
 
 export const SELECT_PARCELLATION = viewerStateSelectParcellation.type
 
 export const DESELECT_REGIONS = `DESELECT_REGIONS`
-export const SELECT_REGIONS = `SELECT_REGIONS`
 export const SELECT_REGIONS_WITH_ID = viewerStateSelectRegionWithIdDeprecated.type
+// export const SET_VIEWER_MODE = viewerStateSetViewerMode.type
 export const SELECT_LANDMARKS = `SELECT_LANDMARKS`
+export const SELECT_REGIONS = viewerStateSetSelectedRegions.type
 export const DESELECT_LANDMARKS = `DESELECT_LANDMARKS`
 export const USER_LANDMARKS = `USER_LANDMARKS`
 
-export const ADD_TO_REGIONS_SELECTION_WITH_IDS = `ADD_TO_REGIONS_SELECTION_WITH_IDS`
-
-export const NEHUBA_LAYER_CHANGED = `NEHUBA_LAYER_CHANGED`
 export const SET_CONNECTIVITY_REGION = `SET_CONNECTIVITY_REGION`
 export const CLEAR_CONNECTIVITY_REGION = `CLEAR_CONNECTIVITY_REGION`
 export const SET_OVERWRITTEN_COLOR_MAP = `SET_OVERWRITTEN_COLOR_MAP`
@@ -378,8 +388,8 @@ export class ViewerStateUseEffect {
         startWith([]),
       )),
       map(([{ segments }, regionsSelected]) => {
-        const selectedSet = new Set(regionsSelected.map(generateLabelIndexId))
-        const toggleArr = segments.map(({ segment, layer }) => generateLabelIndexId({ ngId: layer.name, ...segment }))
+        const selectedSet = new Set<string>(regionsSelected.map(serialiseParcellationRegion))
+        const toggleArr = segments.map(({ segment, layer }) => serialiseParcellationRegion({ ngId: layer.name, ...segment }))
 
         const deleteFlag = toggleArr.some(id => selectedSet.has(id))
 
@@ -387,10 +397,9 @@ export class ViewerStateUseEffect {
           if (deleteFlag) { selectedSet.delete(id) } else { selectedSet.add(id) }
         }
 
-        return {
-          type: SELECT_REGIONS_WITH_ID,
+        return viewerStateSelectRegionWithIdDeprecated({
           selectRegionIds: [...selectedSet],
-        }
+        })
       }),
     )
 
@@ -408,10 +417,9 @@ export class ViewerStateUseEffect {
           ? selectedSpatialDatas.filter((_, idx) => idx !== selectedIdx)
           : selectedSpatialDatas.concat(landmark)
 
-        return {
-          type: SELECT_LANDMARKS,
+        return actionSelectLandmarks({
           landmarks: newSelectedSpatialDatas,
-        }
+        })
       }),
     )
 
diff --git a/src/services/state/viewerState/actions.ts b/src/services/state/viewerState/actions.ts
index 1b2efd6f7c8c726e45d1c95a2877b3b190be0a68..74d44f9b86e3c5bf2327f00b64a3581f9317e436 100644
--- a/src/services/state/viewerState/actions.ts
+++ b/src/services/state/viewerState/actions.ts
@@ -3,7 +3,7 @@ import { IRegion } from './constants'
 
 export const viewerStateNewViewer = createAction(
   `[viewerState] newViewer`,
-  props<{ 
+  props<{
     selectTemplate: any
     selectParcellation: any
     navigation?: any
@@ -25,6 +25,10 @@ export const viewerStateSetConnectivityRegion = createAction(
   props<{ connectivityRegion: any }>()
 )
 
+export const viewerStateNehubaLayerchanged = createAction(
+  `[viewerState] nehubaLayerChanged`,
+)
+
 export const viewerStateNavigateToRegion = createAction(
   `[viewerState] navigateToRegion`,
   props<{ payload: { region: any } }>()
@@ -42,7 +46,14 @@ export const viewerStateSetFetchedAtlases = createAction(
 
 export const viewerStateSelectAtlas = createAction(
   `[viewerState] selectAtlas`,
-  props<{ atlas: { ['@id']: string } }>()
+  props<{
+    atlas: {
+      ['@id']: string
+      template?: {
+        ['@id']: string
+      }
+    }
+  }>()
 )
 
 export const viewerStateHelperSelectParcellationWithId = createAction(
@@ -56,7 +67,7 @@ export const viewerStateSelectParcellation = createAction(
 )
 
 export const viewerStateSelectTemplateWithName = createAction(
-  `[viewerState] selectTemplateWithName`, 
+  `[viewerState] selectTemplateWithName`,
   props<{ payload: { name: string } }>()
 )
 
@@ -77,7 +88,12 @@ export const viewerStateRemoveAdditionalLayer = createAction(
 
 export const viewerStateSelectRegionWithIdDeprecated = createAction(
   `[viewerState] [deprecated] selectRegionsWithId`,
-  props<{ selectRegionIds: number[] }>()
+  props<{ selectRegionIds: string[] }>()
+)
+
+export const viewerStateSetViewerMode = createAction(
+  `[viewerState] setViewerMode`,
+  props<{payload: string}>()
 )
 
 export const viewerStateDblClickOnViewer = createAction(
@@ -109,3 +125,22 @@ export const viewerStateChangeNavigation = createAction(
   `[viewerState] changeNavigation`,
   props<{ navigation: any }>()
 )
+
+export const actionSetMobileUi = createAction(
+  `[viewerState] setMobileUi`,
+  props<{ payload: { useMobileUI: boolean } }>()
+)
+
+export const actionAddToRegionsSelectionWithIds = createAction(
+  `[viewerState] addToRegionSelectionWithIds`,
+  props<{
+    selectRegionIds: string[]
+  }>()
+)
+
+export const actionSelectLandmarks = createAction(
+  `[viewerState] selectLandmarks`,
+  props<{
+    landmarks: any[]
+  }>()
+)
diff --git a/src/services/state/viewerState/selectors.spec.ts b/src/services/state/viewerState/selectors.spec.ts
index 724d3a76b56cbe43c531a1dbbfd059a1dfd11d53..b763ae6c072a1caa37875fbc5aa04f88238181c7 100644
--- a/src/services/state/viewerState/selectors.spec.ts
+++ b/src/services/state/viewerState/selectors.spec.ts
@@ -14,6 +14,7 @@ const allenTemplates = require('!json-loader!src/res/ext/allenMouse.json')
 const colinTemplates = require('!json-loader!src/res/ext/colin.json')
 const mniTemplates = require('!json-loader!src/res/ext/MNI152.json')
 const bbTemplates = require('!json-loader!src/res/ext/bigbrain.json')
+const freesurfer = require('!json-loader!src/res/ext/freesurfer.json')
 
 const fetchedAtlases = [
   waxholmAtlasJson,
@@ -27,27 +28,32 @@ const fetchedTemplates = [
   colinTemplates,
   mniTemplates,
   bbTemplates,
+  freesurfer,
 ]
 
 describe('viewerState/selector.ts', () => {
   describe('> viewerStateGetOverlayingAdditionalParcellations', () => {
-    it('> if atlas has no basic layer, should return empty array', () => {
-      const waxholmParcs = viewerStateGetOverlayingAdditionalParcellations.projector({
-        fetchedAtlases,
-        selectedAtlasId: waxholmAtlasJson['@id']
-      }, {
-        parcellationSelected: waxholmAtlasJson.parcellations[0]
+    describe('> if atlas has no basic layer, should return empty array', () => {
+      it('> true for waxholm', () => {
+        const waxholmParcs = viewerStateGetOverlayingAdditionalParcellations.projector({
+          fetchedAtlases,
+          selectedAtlasId: waxholmAtlasJson['@id']
+        }, {
+          parcellationSelected: waxholmAtlasJson.parcellations[0]
+        })
+        
+        expect(waxholmParcs).toEqual([])
       })
-      
-      expect(waxholmParcs).toEqual([])
-
-      const allenParcs = viewerStateGetOverlayingAdditionalParcellations.projector({
-        fetchedAtlases,
-        selectedAtlasId: allenAtlasJson['@id']
-      }, {
-        parcellationSelected: allenAtlasJson.parcellations[0]
+      it('> true for allen', () => {
+  
+        const allenParcs = viewerStateGetOverlayingAdditionalParcellations.projector({
+          fetchedAtlases,
+          selectedAtlasId: allenAtlasJson['@id']
+        }, {
+          parcellationSelected: allenAtlasJson.parcellations[0]
+        })
+        expect(allenParcs).toEqual([])
       })
-      expect(allenParcs).toEqual([])
     })
 
 
@@ -95,10 +101,21 @@ describe('viewerState/selector.ts', () => {
         //TODO compare strict equality of firsthalf+secondhalf with parc
       }
     }
-    it('> should work', () => {
-      check(waxholmAtlasJson, [waxholmTemplates])
-      check(allenAtlasJson, [allenTemplates])
-      check(humanAtlasJson, [bbTemplates, mniTemplates, colinTemplates])
+    describe('> should work', () => {
+      it('> for waxholm', () => {
+        check(waxholmAtlasJson, [waxholmTemplates])
+      })
+      it('> for allen', () => {
+        check(allenAtlasJson, [allenTemplates])
+      })
+      it('> for human', () => {
+        check(humanAtlasJson, [
+          bbTemplates,
+          mniTemplates,
+          colinTemplates,
+          freesurfer
+        ])
+      })
     })
   })
 
diff --git a/src/services/state/viewerState/selectors.ts b/src/services/state/viewerState/selectors.ts
index 7d71b308359e9b59fac9a84d6359017471a44146..61cd2b7dd54d4f3275ea01c5aa63fbebe60e54f7 100644
--- a/src/services/state/viewerState/selectors.ts
+++ b/src/services/state/viewerState/selectors.ts
@@ -18,7 +18,7 @@ const flattenFetchedTemplatesIntoParcellationsReducer = (acc, curr) => {
       useTheme: curr['useTheme']
     }
   })
-  
+
   return acc.concat( parcelations )
 }
 
@@ -29,7 +29,12 @@ export const viewerStateFetchedTemplatesSelector = createSelector(
 
 export const viewerStateSelectedTemplateSelector = createSelector(
   state => state['viewerState'],
-  viewerState => viewerState['templateSelected']
+  viewerState => viewerState?.['templateSelected']
+)
+
+export const viewerStateSelectorStandaloneVolumes = createSelector(
+  state => state['viewerState'],
+  viewerState => viewerState['standaloneVolumes']
 )
 
 /**
@@ -86,6 +91,11 @@ export const viewerStateSelectorNavigation = createSelector(
   viewerState => viewerState['navigation']
 )
 
+export const viewerStateViewerModeSelector = createSelector(
+  state => state['viewerState'],
+  viewerState => viewerState['viewerMode']
+)
+
 export const viewerStateGetOverlayingAdditionalParcellations = createSelector(
   state => state[viewerStateHelperStoreName],
   state => state['viewerState'],
@@ -200,5 +210,22 @@ export const viewerStateSelectedTemplateFullInfoSelector = createSelector(
         darktheme: (fullTemplateInfo || {}).useTheme === 'dark'
       }
     })
-  } 
+  }
+)
+
+export const viewerStateContextedSelectedRegionsSelector = createSelector(
+  viewerStateSelectedRegionsSelector,
+  viewerStateGetSelectedAtlas,
+  viewerStateSelectedTemplatePureSelector,
+  viewerStateSelectedParcellationSelector,
+  (regions, atlas, template, parcellation) => regions.map(r => {
+    return {
+      ...r,
+      context: {
+        atlas,
+        template,
+        parcellation
+      }
+    }
+  })
 )
diff --git a/src/services/stateStore.service.ts b/src/services/stateStore.service.ts
index 1dcde5194a5a12f343646f0f056b4574265bcf60..8a3d26cd970f83eb53bd1fd04fae703175caec1e 100644
--- a/src/services/stateStore.service.ts
+++ b/src/services/stateStore.service.ts
@@ -1,5 +1,9 @@
 import { filter } from 'rxjs/operators';
 
+export {
+  recursiveFindRegionWithLabelIndexId
+} from 'src/util/fn'
+
 export { getNgIds } from 'src/util/fn'
 
 import {
@@ -23,7 +27,7 @@ import {
   ACTION_TYPES as USER_CONFIG_ACTION_TYPES,
   defaultState as userConfigDefaultState,
   StateInterface as UserConfigStateInterface,
-  stateStore as userConfigState,
+  userConfigReducer as userConfigState,
 } from './state/userConfigState.store'
 import {
   defaultState as viewerConfigDefaultState,
@@ -49,9 +53,9 @@ export { ViewerStateInterface, ViewerActionInterface, viewerState }
 export { IUiState, UIActionInterface, uiState }
 export { userConfigState,  USER_CONFIG_ACTION_TYPES}
 
-export { CHANGE_NAVIGATION, DESELECT_LANDMARKS, FETCHED_TEMPLATE, NEWVIEWER, SELECT_LANDMARKS, SELECT_PARCELLATION, SELECT_REGIONS, USER_LANDMARKS } from './state/viewerState.store'
+export { CHANGE_NAVIGATION, DESELECT_LANDMARKS, FETCHED_TEMPLATE, SELECT_PARCELLATION, SELECT_REGIONS, USER_LANDMARKS } from './state/viewerState.store'
 export { IDataEntry, IParcellationRegion, FETCHED_DATAENTRIES, FETCHED_SPATIAL_DATA, ILandmark, IOtherLandmarkGeometry, IPlaneLandmarkGeometry, IPointLandmarkGeometry, IProperty, IPublication, IReferenceSpace, IFile, IFileSupplementData } from './state/dataStore.store'
-export { CLOSE_SIDE_PANEL, MOUSE_OVER_LANDMARK, MOUSE_OVER_SEGMENT, OPEN_SIDE_PANEL, COLLAPSE_SIDE_PANEL_CURRENT_VIEW, EXPAND_SIDE_PANEL_CURRENT_VIEW } from './state/uiState.store'
+export { CLOSE_SIDE_PANEL, MOUSE_OVER_SEGMENT, OPEN_SIDE_PANEL, COLLAPSE_SIDE_PANEL_CURRENT_VIEW, EXPAND_SIDE_PANEL_CURRENT_VIEW } from './state/uiState.store'
 export { UserConfigStateUseEffect } from './state/userConfigState.store'
 
 export { GENERAL_ACTION_TYPES, generalActionError } from './stateStore.helper'
@@ -63,12 +67,6 @@ export function safeFilter(key: string) {
     typeof state[key] !== 'undefined' && state[key] !== null)
 }
 
-export function getNgIdLabelIndexFromRegion({ region }) {
-  const { ngId, labelIndex } = region
-  if (ngId && labelIndex) { return { ngId, labelIndex } }
-  throw new Error(`ngId: ${ngId} or labelIndex: ${labelIndex} not defined`)
-}
-
 export function getMultiNgIdsRegionsLabelIndexMap(parcellation: any = {}, inheritAttrsOpt: any = { ngId: 'root' }): Map<string, Map<number, any>> {
   const map: Map<string, Map<number, any>> = new Map()
   
@@ -149,41 +147,6 @@ export function isDefined(obj) {
   return typeof obj !== 'undefined' && obj !== null
 }
 
-export function generateLabelIndexId({ ngId, labelIndex }) {
-  return `${ngId}#${labelIndex}`
-}
-
-export function getNgIdLabelIndexFromId({ labelIndexId } = {labelIndexId: ''}) {
-  const _ = labelIndexId && labelIndexId.split && labelIndexId.split('#') || []
-  const ngId = _.length > 1
-    ? _[0]
-    : null
-  const labelIndex = _.length > 1
-    ? Number(_[1])
-    : _.length === 0
-      ? null
-      : Number(_[0])
-  return { ngId, labelIndex }
-}
-
-const recursiveFlatten = (region, {ngId}) => {
-  return [{
-    ngId,
-    ...region,
-  }].concat(
-    ...((region.children && region.children.map && region.children.map(c => recursiveFlatten(c, { ngId : region.ngId || ngId })) ) || []),
-  )
-}
-
-export function recursiveFindRegionWithLabelIndexId({ regions, labelIndexId, inheritedNgId = 'root' }: {regions: any[], labelIndexId: string, inheritedNgId: string}) {
-  const { ngId, labelIndex } = getNgIdLabelIndexFromId({ labelIndexId })
-  const fr1 = regions.map(r => recursiveFlatten(r, { ngId: inheritedNgId }))
-  const fr2 = fr1.reduce((acc, curr) => acc.concat(...curr), [])
-  const found = fr2.find(r => r.ngId === ngId && Number(r.labelIndex) === Number(labelIndex))
-  if (found) { return found }
-  return null
-}
-
 export interface IavRootStoreInterface {
   pluginState: PluginStateInterface
   viewerConfigState: ViewerConfigStateInterface
@@ -194,7 +157,7 @@ export interface IavRootStoreInterface {
   userConfigState: UserConfigStateInterface
 }
 
-import { DATASTORE_DEFAULT_STATE } from 'src/ui/databrowserModule'
+import { DATASTORE_DEFAULT_STATE } from 'src/atlasComponents/databrowserModule'
 
 export const defaultRootState: any = {
   pluginState: pluginDefaultState,
diff --git a/src/services/templateCoordinatesTransformation.service.spec.ts b/src/services/templateCoordinatesTransformation.service.spec.ts
index 08906f068f5d49f38d65d599e6bfe8a099545751..0aa477d103f4dfe8b0894314e4a08bfde4666cfb 100644
--- a/src/services/templateCoordinatesTransformation.service.spec.ts
+++ b/src/services/templateCoordinatesTransformation.service.spec.ts
@@ -33,8 +33,8 @@ describe('templateCoordinatesTransformation.service.spec.ts', () => {
 
         // subscriptions are necessary for http fetch to occur
         service.getPointCoordinatesForTemplate(
-          'from',
-          'target',
+          TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.MNI152,
+          TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN,
           [1,2,3]
         ).subscribe((_ev) => {
           
@@ -44,8 +44,8 @@ describe('templateCoordinatesTransformation.service.spec.ts', () => {
         expect(
           JSON.parse(req.request.body)
         ).toEqual({
-          'source_space': 'from',
-          'target_space': 'target',
+          'source_space': TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.MNI152,
+          'target_space': TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN,
           'source_points': [
             [1e-6, 2e-6, 3e-6]
           ]
@@ -53,14 +53,41 @@ describe('templateCoordinatesTransformation.service.spec.ts', () => {
         req.flush({})
       })
 
+      it('transforms mapped space name(s)', () => {
+        const service = TestBed.inject(TemplateCoordinatesTransformation)
+        const httpTestingController = TestBed.inject(HttpTestingController)
+
+        const key = Array.from(TemplateCoordinatesTransformation.NameMap.keys())[0]
+        service.getPointCoordinatesForTemplate(
+          key,
+          TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN,
+          [1,2,3]
+        ).subscribe((_ev) => {
+          
+        })
+        const req = httpTestingController.expectOne(service.url)
+        expect(req.request.method).toEqual('POST')
+        expect(
+          JSON.parse(req.request.body)
+        ).toEqual({
+          'source_space': TemplateCoordinatesTransformation.NameMap.get(key),
+          'target_space': TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN,
+          'source_points': [
+            [1e-6, 2e-6, 3e-6]
+          ]
+        })
+        req.flush({})
+      })
+
+
       it('should transform response properly', () => {
 
         const service = TestBed.inject(TemplateCoordinatesTransformation)
         const httpTestingController = TestBed.inject(HttpTestingController)
 
         service.getPointCoordinatesForTemplate(
-          'from',
-          'target',
+          TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.MNI152,
+          TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN,
           [1,2,3]
         ).subscribe(({ status, result }) => {
           expect(status).toEqual('completed')
@@ -79,8 +106,8 @@ describe('templateCoordinatesTransformation.service.spec.ts', () => {
         const httpTestingController = TestBed.inject(HttpTestingController)
 
         service.getPointCoordinatesForTemplate(
-          'from',
-          'target',
+          TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.MNI152,
+          TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN,
           [1,2,3]
         ).subscribe(({ status }) => {
           expect(status).toEqual('error')
@@ -96,8 +123,8 @@ describe('templateCoordinatesTransformation.service.spec.ts', () => {
         const httpTestingController = TestBed.inject(HttpTestingController)
 
         service.getPointCoordinatesForTemplate(
-          'from',
-          'target',
+          TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.MNI152,
+          TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN,
           [1,2,3]
         ).subscribe(({ status, statusText }) => {
           expect(status).toEqual('error')
diff --git a/src/services/templateCoordinatesTransformation.service.ts b/src/services/templateCoordinatesTransformation.service.ts
index 6aaf98b99adb5fa613da758af51f37ab82ef0e54..2c6f4fce9048306566b50be5a0f14a25a27a5f9d 100644
--- a/src/services/templateCoordinatesTransformation.service.ts
+++ b/src/services/templateCoordinatesTransformation.service.ts
@@ -14,6 +14,18 @@ export interface ITemplateCoordXformResp{
 })
 export class TemplateCoordinatesTransformation {
 
+  static VALID_TEMPLATE_SPACE_NAMES = {
+    MNI152: 'MNI 152 ICBM 2009c Nonlinear Asymmetric',
+    COLIN27: 'MNI Colin 27',
+    BIG_BRAIN: 'Big Brain (Histology)',
+    INFANT: 'Infant Atlas',
+  }
+
+  static NameMap = new Map([
+    ['MNI152 2009c nonl asym', TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.MNI152],
+    ["Big Brain", TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN]
+  ])
+
   constructor(private httpClient: HttpClient) {}
 
   public url = `${SPATIAL_TRANSFORM_BACKEND.replace(/\/$/, '')}/v1/transform-points`
@@ -27,12 +39,20 @@ export class TemplateCoordinatesTransformation {
         'Content-Type':  'application/json'
       })
     }
+    const srcTmplName = Object.values(TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES).includes(sourceTemplateName)
+      ? sourceTemplateName
+      : TemplateCoordinatesTransformation.NameMap.get(sourceTemplateName)
+
+    const targetTmplName = Object.values(TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES).includes(targetTemplateName)
+      ? targetTemplateName
+      : TemplateCoordinatesTransformation.NameMap.get(targetTemplateName)
+    
     return this.httpClient.post(
       this.url,
       JSON.stringify({
         'source_points': [[...coordinatesInNm.map(c => c/1e6)]],
-        'source_space': sourceTemplateName,
-        'target_space': targetTemplateName
+        'source_space': srcTmplName,
+        'target_space': targetTmplName
       }),
       httpOptions
     ).pipe(
diff --git a/src/services/uiService.service.ts b/src/services/uiService.service.ts
index dc6f4dda7ea8c427536f2b567e94ef2b24e726ab..cc8774728fb1742e36722135960341c35f7c0599 100644
--- a/src/services/uiService.service.ts
+++ b/src/services/uiService.service.ts
@@ -1,5 +1,4 @@
 import { Injectable } from "@angular/core";
-import { ToastHandler } from "src/util/pluginHandlerClasses/toastHandler";
 import {MatSnackBar, MatSnackBarConfig} from "@angular/material/snack-bar";
 import { MatDialog } from "@angular/material/dialog";
 import { ActionDialog } from "src/ui/actionDialog/actionDialog.component";
@@ -15,22 +14,6 @@ export class UIService {
   ) {
   }
 
-  public getToastHandler = () => {
-    const toasthandler = new ToastHandler()
-    let handle
-    toasthandler.show = () => {
-      handle = this.showMessage(toasthandler.message, null, {
-        duration: toasthandler.timeout,
-      })
-    }
-
-    toasthandler.hide = () => {
-      if (handle) { handle.dismiss() }
-      handle = null
-    }
-    return toasthandler
-  }
-
   public showMessage(message: string, actionBtnTxt: string = 'Dismiss', config?: Partial<MatSnackBarConfig>) {
     return this.snackbar.open(message, actionBtnTxt, config)
   }
diff --git a/src/spotlight/sl-service.service.ts b/src/spotlight/sl-service.service.ts
index c045ebfdbeb4ef5f2e5f1c682bf814cd7718dda1..241fe068cb335726119d8532d31d3a06ff6351f1 100644
--- a/src/spotlight/sl-service.service.ts
+++ b/src/spotlight/sl-service.service.ts
@@ -21,6 +21,9 @@ export class SlServiceService implements OnDestroy{
     this.cf = cfr.resolveComponentFactory(SpotlightBackdropComponent)
   }
 
+  /**
+   * TODO use angular cdk overlay
+   */
   public showBackdrop(tmp?: TemplateRef<any>){
     this.hideBackdrop()
 
diff --git a/src/ui/viewerStateController/viewerState.useEffect.spec.ts b/src/state/effects/viewerState.useEffect.spec.ts
similarity index 53%
rename from src/ui/viewerStateController/viewerState.useEffect.spec.ts
rename to src/state/effects/viewerState.useEffect.spec.ts
index b38a7db90370f82ebe1c53b4811cefb053e0555a..e04c7971702840e49916ccfb847a4b36c301bc65 100644
--- a/src/ui/viewerStateController/viewerState.useEffect.spec.ts
+++ b/src/state/effects/viewerState.useEffect.spec.ts
@@ -1,5 +1,5 @@
-import { cvtNavigationObjToNehubaConfig, cvtNehubaConfigToNavigationObj, ViewerStateControllerUseEffect, defaultNavigationObject, defaultNehubaConfigObject } from './viewerState.useEffect'
-import { Observable, of } from 'rxjs'
+import { cvtNehubaConfigToNavigationObj, ViewerStateControllerUseEffect, defaultNavigationObject } from './viewerState.useEffect'
+import { Observable, of, throwError } from 'rxjs'
 import { TestBed, async } from '@angular/core/testing'
 import { provideMockActions } from '@ngrx/effects/testing'
 import { MockStore, provideMockStore } from '@ngrx/store/testing'
@@ -7,13 +7,15 @@ import { defaultRootState, generalActionError } from 'src/services/stateStore.se
 import { Injectable } from '@angular/core'
 import { TemplateCoordinatesTransformation, ITemplateCoordXformResp } from 'src/services/templateCoordinatesTransformation.service'
 import { hot } from 'jasmine-marbles'
-import { AngularMaterialModule } from '../sharedModules/angularMaterial.module'
+import { AngularMaterialModule } from 'src/ui/sharedModules/angularMaterial.module'
 import { HttpClientModule } from '@angular/common/http'
 import { WidgetModule } from 'src/widget'
-import { PluginModule } from 'src/atlasViewer/pluginUnit/plugin.module'
+import { PluginModule } from 'src/plugin'
 import { viewerStateFetchedTemplatesSelector, viewerStateNavigateToRegion, viewerStateNavigationStateSelector, viewerStateNewViewer, viewerStateSelectAtlas, viewerStateSelectTemplateWithName } from 'src/services/state/viewerState.store.helper'
-import { viewerStateFetchedAtlasesSelector } from 'src/services/state/viewerState/selectors'
+import { viewerStateFetchedAtlasesSelector, viewerStateGetSelectedAtlas, viewerStateSelectedParcellationSelector, viewerStateSelectedTemplateSelector } from 'src/services/state/viewerState/selectors'
 import { CONST } from 'common/constants'
+import { PureContantService } from 'src/util'
+import { viewerStateChangeNavigation } from 'src/services/state/viewerState/actions'
 
 const bigbrainJson = require('!json-loader!src/res/ext/bigbrain.json')
 const bigBrainNehubaConfig = require('!json-loader!src/res/ext/bigbrainNehubaConfig.json')
@@ -56,6 +58,16 @@ const currentNavigation = {
 }
 initialState.viewerState.navigation = currentNavigation
 
+class MockPureConstantService{
+  allFetchingReady$ = of(true)
+  initFetchTemplate$ = of([])
+
+  getRegionDetail(){
+    return of(null)
+  }
+}
+
+const mockPureConstantService = new MockPureConstantService()
 describe('> viewerState.useEffect.ts', () => {
   describe('> ViewerStateControllerUseEffect', () => {
     let actions$: Observable<any>
@@ -80,6 +92,10 @@ describe('> viewerState.useEffect.ts', () => {
           {
             provide: TemplateCoordinatesTransformation,
             useValue: mock
+          },
+          {
+            provide: PureContantService,
+            useValue: mockPureConstantService
           }
         ]
       }).compileComponents()
@@ -244,107 +260,223 @@ describe('> viewerState.useEffect.ts', () => {
           }
         )
       }
-      describe('> if the region has malformed position property', () => {
-        describe('> if the region has no position property', () => {
+      let mockStore: MockStore
+      beforeEach(() => {
+
+        mockStore = TestBed.inject(MockStore)
+          
+        mockStore.overrideSelector(viewerStateGetSelectedAtlas, { '@id': 'foo-bar-atlas'})
+        mockStore.overrideSelector(viewerStateSelectedTemplateSelector, { '@id': 'foo-bar-template'})
+        mockStore.overrideSelector(viewerStateSelectedParcellationSelector, { '@id': 'foo-bar-parcellation'})
+      })
+      describe('> if atlas, template, parc is not set', () => {
+        beforeEach(() => {
           const region = {
-            name: 'foobar'
+            name: 'foo bar'
           }
+          setAction(region)
+        })
+        describe('> if atlas is unset', () => {
           beforeEach(() => {
-            setAction(region)
+            mockStore.overrideSelector(viewerStateGetSelectedAtlas, null)
           })
-
-          it('> should result in general action error', () => {
-            const ctrlUseEffect = TestBed.inject(ViewerStateControllerUseEffect)
-            expect(ctrlUseEffect.navigateToRegion$).toBeObservable(
+          it('> returns general error', () => {
+            const effect = TestBed.inject(ViewerStateControllerUseEffect)
+            expect(effect.navigateToRegion$).toBeObservable(
               hot('a', {
                 a: generalActionError({
-                  message: `${region.name} - does not have a position defined`
+                  message: 'Go to region: region / atlas / template / parcellation not defined.'
                 })
               })
             )
           })
-        
-          describe('> if the region has non array position property', () => {
-            const region = {
-              name: 'foo bar2',
-              position: {'hello': 'world'}
-            }
-            beforeEach(() => {
-              setAction(region)
+        })
+        describe('> if template is unset', () => {
+          beforeEach(() => {
+            mockStore.overrideSelector(viewerStateSelectedTemplateSelector, null)
+          })
+          it('> returns general error', () => {
+            const effect = TestBed.inject(ViewerStateControllerUseEffect)
+            expect(effect.navigateToRegion$).toBeObservable(
+              hot('a', {
+                a: generalActionError({
+                  message: 'Go to region: region / atlas / template / parcellation not defined.'
+                })
+              })
+            )
+          })
+        })
+        describe('> if parc is unset', () => {
+          beforeEach(() => {
+            mockStore.overrideSelector(viewerStateSelectedParcellationSelector, null)
+          })
+          it('> returns general error', () => {
+            const effect = TestBed.inject(ViewerStateControllerUseEffect)
+            expect(effect.navigateToRegion$).toBeObservable(
+              hot('a', {
+                a: generalActionError({
+                  message: 'Go to region: region / atlas / template / parcellation not defined.'
+                })
+              })
+            )
+          })
+        })
+      })
+      describe('> if atlas, template, parc is set, but region unset', () => {
+        beforeEach(() => {
+          setAction(null)
+        })
+        it('> returns general error', () => {
+          const effect = TestBed.inject(ViewerStateControllerUseEffect)
+          expect(effect.navigateToRegion$).toBeObservable(
+            hot('a', {
+              a: generalActionError({
+                message: 'Go to region: region / atlas / template / parcellation not defined.'
+              })
+            })
+          )
+        })
+      })
+
+      describe('> if inputs are fine', () => {
+        let getRegionDetailSpy: jasmine.Spy
+        const region = {
+          name: 'foo bar'
+        }
+        beforeEach(() => {
+          getRegionDetailSpy = spyOn(mockPureConstantService, 'getRegionDetail').and.callThrough()
+          setAction(region)
+        })
+        afterEach(() => {
+          getRegionDetailSpy.calls.reset()
+        })
+
+        it('> getRegionDetailSpy is called', () => {
+          const ctrl = TestBed.inject(ViewerStateControllerUseEffect)
+
+          // necessary to trigger the emit
+          expect(
+            ctrl.navigateToRegion$
+          ).toBeObservable(
+            hot('a', {
+              a: generalActionError({
+                message: 'Fetching region detail error: Error: region detail not found!'
+              })
             })
-            it('> should result in general action error', () => {
-              const ctrlUseEffect = TestBed.inject(ViewerStateControllerUseEffect)
-              expect(ctrlUseEffect.navigateToRegion$).toBeObservable(
+          )
+
+          expect(getRegionDetailSpy).toHaveBeenCalled()
+        })
+
+        describe('> mal formed return', () => {
+          describe('> returns null', () => {
+            it('> generalactionerror', () => {
+              const ctrl = TestBed.inject(ViewerStateControllerUseEffect)
+              expect(
+                ctrl.navigateToRegion$
+              ).toBeObservable(
                 hot('a', {
                   a: generalActionError({
-                    message: `${region.name} has malformed position property: ${JSON.stringify(region.position)}`
+                    message: 'Fetching region detail error: Error: region detail not found!'
                   })
                 })
               )
             })
           })
-        
-          describe('> if the region has array position, but not all elements are number', () => {
-            const region = {
-              name: 'foo bar2',
-              position: [0, 1, 'hello world']
-            }
+          describe('> general throw', () => {
+            const msg = 'oh no!'
             beforeEach(() => {
-              setAction(region)
+              getRegionDetailSpy.and.callFake(() => throwError(msg))
             })
-            it('> should result in general action error', () => {
-              const ctrlUseEffect = TestBed.inject(ViewerStateControllerUseEffect)
-              expect(ctrlUseEffect.navigateToRegion$).toBeObservable(
+
+            it('> generalactionerror', () => {
+              const ctrl = TestBed.inject(ViewerStateControllerUseEffect)
+              expect(
+                ctrl.navigateToRegion$
+              ).toBeObservable(
                 hot('a', {
                   a: generalActionError({
-                    message: `${region.name} has malformed position property: ${JSON.stringify(region.position)}`
+                    message: `Fetching region detail error: ${msg}`
                   })
                 })
               )
             })
+
           })
-        
-          describe('> if the region has array position, but some elements are NaN', () => {
-            const region = {
-              name: 'foo bar2',
-              position: [0, 1, NaN]
-            }
+          describe('> does not contain props attr', () => {
+
             beforeEach(() => {
-              setAction(region)
+              getRegionDetailSpy.and.callFake(() => of({
+                name: 'foo-bar'
+              }))
             })
-            it('> should result in general action error', () => {
-              const ctrlUseEffect = TestBed.inject(ViewerStateControllerUseEffect)
-              expect(ctrlUseEffect.navigateToRegion$).toBeObservable(
+
+            it('> generalactionerror', () => {
+              const ctrl = TestBed.inject(ViewerStateControllerUseEffect)
+              expect(
+                ctrl.navigateToRegion$
+              ).toBeObservable(
                 hot('a', {
                   a: generalActionError({
-                    message: `${region.name} has malformed position property: ${JSON.stringify(region.position)}`
+                    message: `Fetching region detail error: Error: region does not have props defined!`
                   })
                 })
               )
             })
           })
-        
-        
-          describe('> if the region has array position, with incorrect length', () => {
-            const region = {
-              name: 'foo bar2',
-              position: []
-            }
+
+          describe('> does not contain props.length === 0', () => {
+
             beforeEach(() => {
-              setAction(region)
+              getRegionDetailSpy.and.callFake(() => of({
+                name: 'foo-bar',
+                props: []
+              }))
             })
-            it('> should result in general action error', () => {
-              const ctrlUseEffect = TestBed.inject(ViewerStateControllerUseEffect)
-              expect(ctrlUseEffect.navigateToRegion$).toBeObservable(
+
+            it('> generalactionerror', () => {
+              const ctrl = TestBed.inject(ViewerStateControllerUseEffect)
+              expect(
+                ctrl.navigateToRegion$
+              ).toBeObservable(
                 hot('a', {
                   a: generalActionError({
-                    message: `${region.name} has malformed position property: ${JSON.stringify(region.position)}`
+                    message: `Fetching region detail error: Error: region props not found!`
+                  })
+                })
+              )
+            })
+          })
+        })
+
+        describe('> wellformed response', () => {
+          beforeEach(() => {
+
+            beforeEach(() => {
+              getRegionDetailSpy.and.callFake(() => of({
+                name: 'foo-bar',
+                props: [{
+                  centroid_mm: [1,2,3]
+                }]
+              }))
+            })
+
+            it('> emits viewerStateChangeNavigation', () => {
+              const ctrl = TestBed.inject(ViewerStateControllerUseEffect)
+              expect(
+                ctrl.navigateToRegion$
+              ).toBeObservable(
+                hot('a', {
+                  a: viewerStateChangeNavigation({
+                    navigation: {
+                      position: [1e6,2e6,3e6],
+                      animation: {}
+                    }
                   })
                 })
               )
             })
           })
-        
         })
       })
     })
@@ -378,7 +510,7 @@ describe('> viewerState.useEffect.ts', () => {
         )
       })
     
-      describe('> if atlas found, will try to find id of first template', () => {
+      describe('> if atlas found', () => {
         const mockParc1 = {
           ['@id']: 'parc-1',
           availableIn: [{
@@ -399,70 +531,155 @@ describe('> viewerState.useEffect.ts', () => {
           ['@id']: 'test-1',
           availableIn: [ mockParc1 ]
         }
-        it('> if fails, will return general error', () => {
-
-          mockStore.overrideSelector(viewerStateFetchedTemplatesSelector, [
-            mockTmplSpc1
-          ])
-          mockStore.overrideSelector(viewerStateFetchedAtlasesSelector, [{
-            ['@id']: 'foo-bar',
-            templateSpaces: [ mockTmplSpc ]
-          }])
-          actions$ = hot('a', {
-            a: viewerStateSelectAtlas({
-              atlas: {
+
+        describe('> if template key val is not provided', () => {
+          describe('> will try to find the id of the first tmpl', () => {
+
+            it('> if fails, will return general error', () => {
+
+              mockStore.overrideSelector(viewerStateFetchedTemplatesSelector, [
+                mockTmplSpc1
+              ])
+              mockStore.overrideSelector(viewerStateFetchedAtlasesSelector, [{
                 ['@id']: 'foo-bar',
-              }
+                templateSpaces: [ mockTmplSpc ],
+                parcellations: [ mockParc0 ]
+              }])
+              actions$ = hot('a', {
+                a: viewerStateSelectAtlas({
+                  atlas: {
+                    ['@id']: 'foo-bar',
+                  }
+                })
+              })
+              
+              const viewerSTateCtrlEffect = TestBed.inject(ViewerStateControllerUseEffect)
+              expect(
+                viewerSTateCtrlEffect.onSelectAtlasSelectTmplParc$
+              ).toBeObservable(
+                hot('a', {
+                  a: generalActionError({
+                    message: CONST.TEMPLATE_NOT_FOUND
+                  })
+                })
+              )
             })
-          })
           
-          const viewerSTateCtrlEffect = TestBed.inject(ViewerStateControllerUseEffect)
-          expect(
-            viewerSTateCtrlEffect.onSelectAtlasSelectTmplParc$
-          ).toBeObservable(
-            hot('a', {
-              a: generalActionError({
-                message: CONST.TEMPLATE_NOT_FOUND
+            it('> if succeeds, will dispatch new viewer', () => {
+              const completeMocktmpl = {
+                ...mockTmplSpc1,
+                parcellations: [ mockParc1 ]
+              }
+              mockStore.overrideSelector(viewerStateFetchedTemplatesSelector, [
+                completeMocktmpl
+              ])
+              mockStore.overrideSelector(viewerStateFetchedAtlasesSelector, [{
+                ['@id']: 'foo-bar',
+                templateSpaces: [ mockTmplSpc1 ],
+                parcellations: [ mockParc1 ]
+              }])
+              actions$ = hot('a', {
+                a: viewerStateSelectAtlas({
+                  atlas: {
+                    ['@id']: 'foo-bar',
+                  }
+                })
               })
+              
+              const viewerSTateCtrlEffect = TestBed.inject(ViewerStateControllerUseEffect)
+              expect(
+                viewerSTateCtrlEffect.onSelectAtlasSelectTmplParc$
+              ).toBeObservable(
+                hot('a', {
+                  a: viewerStateNewViewer({
+                    selectTemplate: completeMocktmpl,
+                    selectParcellation: mockParc1
+                  })
+                })
+              )
             })
-          )
-        })
       
-        it('> if succeeds, will dispatch new viewer', () => {
-          const completeMocktmpl = {
+          })
+        })
+
+        describe('> if template key val is provided', () => {
+
+          const completeMockTmpl = {
+            ...mockTmplSpc,
+            parcellations: [ mockParc0 ]
+          }
+          const completeMocktmpl1 = {
             ...mockTmplSpc1,
             parcellations: [ mockParc1 ]
           }
-          mockStore.overrideSelector(viewerStateFetchedTemplatesSelector, [
-            completeMocktmpl
-          ])
-          mockStore.overrideSelector(viewerStateFetchedAtlasesSelector, [{
-            ['@id']: 'foo-bar',
-            templateSpaces: [ mockTmplSpc1 ]
-          }])
-          actions$ = hot('a', {
-            a: viewerStateSelectAtlas({
-              atlas: {
-                ['@id']: 'foo-bar',
-              }
+          beforeEach(() => {
+
+            mockStore.overrideSelector(viewerStateFetchedTemplatesSelector, [
+              completeMockTmpl,
+              completeMocktmpl1,
+            ])
+            mockStore.overrideSelector(viewerStateFetchedAtlasesSelector, [{
+              ['@id']: 'foo-bar',
+              templateSpaces: [ mockTmplSpc, mockTmplSpc1 ],
+              parcellations: [ mockParc0, mockParc1 ]
+            }])
+          })
+          it('> will select template.@id', () => {
+
+            actions$ = hot('a', {
+              a: viewerStateSelectAtlas({
+                atlas: {
+                  ['@id']: 'foo-bar',
+                  template: {
+                    ['@id']: mockTmplSpc1['@id']
+                  }
+                }
+              })
             })
+            
+            const viewerSTateCtrlEffect = TestBed.inject(ViewerStateControllerUseEffect)
+            expect(
+              viewerSTateCtrlEffect.onSelectAtlasSelectTmplParc$
+            ).toBeObservable(
+              hot('a', {
+                a: viewerStateNewViewer({
+                  selectTemplate: completeMocktmpl1,
+                  selectParcellation: mockParc1
+                })
+              })
+            )
+
           })
           
-          const viewerSTateCtrlEffect = TestBed.inject(ViewerStateControllerUseEffect)
-          expect(
-            viewerSTateCtrlEffect.onSelectAtlasSelectTmplParc$
-          ).toBeObservable(
-            hot('a', {
-              a: viewerStateNewViewer({
-                selectTemplate: completeMocktmpl,
-                selectParcellation: mockParc1
+          it('> if template.@id is not defined, will fallback to first template', () => {
+
+            actions$ = hot('a', {
+              a: viewerStateSelectAtlas({
+                atlas: {
+                  ['@id']: 'foo-bar',
+                  template: {
+                    
+                  } as any
+                }
               })
             })
-          )
+
+            const viewerSTateCtrlEffect = TestBed.inject(ViewerStateControllerUseEffect)
+            expect(
+              viewerSTateCtrlEffect.onSelectAtlasSelectTmplParc$
+            ).toBeObservable(
+              hot('a', {
+                a: viewerStateNewViewer({
+                  selectTemplate: completeMockTmpl,
+                  selectParcellation: mockParc0
+                })
+              })
+            )
+
+          })
         })
       })
     })
-  
   })
 
   describe('> cvtNehubaConfigToNavigationObj', () => {
@@ -470,14 +687,7 @@ describe('> viewerState.useEffect.ts', () => {
       it('> if no arg is provided', () => {
 
         const obj = cvtNehubaConfigToNavigationObj()
-        expect(obj).toEqual({
-          orientation: [0, 0, 0, 1],
-          perspectiveOrientation: [0 , 0, 0, 1],
-          perspectiveZoom: 1e6,
-          zoom: 1e6,
-          position: [0, 0, 0],
-          positionReal: true
-        })
+        expect(obj).toEqual(defaultNavigationObject)
       })
       it('> if null or undefined is provided', () => {
 
@@ -514,75 +724,5 @@ describe('> viewerState.useEffect.ts', () => {
       })
     })
   })
-  describe('> cvtNavigationObjToNehubaConfig', () => {
-    const validNehubaConfigObj = reconstitutedBigBrain.nehubaConfig.dataset.initialNgState
-    const validNavigationObj = currentNavigation
-    describe('> if inputs are malformed', () => {
-      describe('> if navigation object is malformed, uses navigation default object', () => {
-        it('> if navigation object is null', () => {
-          const v1 = cvtNavigationObjToNehubaConfig(null, validNehubaConfigObj)
-          const v2 = cvtNavigationObjToNehubaConfig(defaultNavigationObject, validNehubaConfigObj)
-          expect(v1).toEqual(v2)
-        })
-        it('> if navigation object is undefined', () => {
-          const v1 = cvtNavigationObjToNehubaConfig(undefined, validNehubaConfigObj)
-          const v2 = cvtNavigationObjToNehubaConfig(defaultNavigationObject, validNehubaConfigObj)
-          expect(v1).toEqual(v2)
-        })
-
-        it('> if navigation object is otherwise malformed', () => {
-          const v1 = cvtNavigationObjToNehubaConfig(reconstitutedBigBrain, validNehubaConfigObj)
-          const v2 = cvtNavigationObjToNehubaConfig(defaultNavigationObject, validNehubaConfigObj)
-          expect(v1).toEqual(v2)
 
-          const v3 = cvtNavigationObjToNehubaConfig({}, validNehubaConfigObj)
-          const v4 = cvtNavigationObjToNehubaConfig(defaultNavigationObject, validNehubaConfigObj)
-          expect(v3).toEqual(v4)
-        })
-      })
-
-      describe('> if nehubaConfig object is malformed, use default nehubaConfig obj', () => {
-        it('> if nehubaConfig is null', () => {
-          const v1 = cvtNavigationObjToNehubaConfig(validNavigationObj, null)
-          const v2 = cvtNavigationObjToNehubaConfig(validNavigationObj, defaultNehubaConfigObject)
-          expect(v1).toEqual(v2)
-        })
-
-        it('> if nehubaConfig is undefined', () => {
-          const v1 = cvtNavigationObjToNehubaConfig(validNavigationObj, undefined)
-          const v2 = cvtNavigationObjToNehubaConfig(validNavigationObj, defaultNehubaConfigObject)
-          expect(v1).toEqual(v2)
-        })
-
-        it('> if nehubaConfig is otherwise malformed', () => {
-          const v1 = cvtNavigationObjToNehubaConfig(validNavigationObj, {})
-          const v2 = cvtNavigationObjToNehubaConfig(validNavigationObj, defaultNehubaConfigObject)
-          expect(v1).toEqual(v2)
-
-          const v3 = cvtNavigationObjToNehubaConfig(validNavigationObj, reconstitutedBigBrain)
-          const v4 = cvtNavigationObjToNehubaConfig(validNavigationObj, defaultNehubaConfigObject)
-          expect(v3).toEqual(v4)
-        })
-      })
-    })
-    it('> converts navigation object and reference nehuba config object to navigation object', () => {
-      const convertedVal = cvtNavigationObjToNehubaConfig(validNavigationObj, validNehubaConfigObj)
-      const { perspectiveOrientation, orientation, zoom, perspectiveZoom, position } = validNavigationObj
-      
-      expect(convertedVal).toEqual({
-        navigation: {
-          pose: {
-            position: {
-              voxelSize: validNehubaConfigObj.navigation.pose.position.voxelSize,
-              voxelCoordinates: [0, 1, 2].map(idx => position[idx] / validNehubaConfigObj.navigation.pose.position.voxelSize[idx])
-            },
-            orientation
-          },
-          zoomFactor: zoom
-        },
-        perspectiveOrientation: perspectiveOrientation,
-        perspectiveZoom: perspectiveZoom
-      })
-    })
-  })
 })
diff --git a/src/ui/viewerStateController/viewerState.useEffect.ts b/src/state/effects/viewerState.useEffect.ts
similarity index 77%
rename from src/ui/viewerStateController/viewerState.useEffect.ts
rename to src/state/effects/viewerState.useEffect.ts
index 18eef3feafbaebc9d4bdf05b60447a6619ce870c..0764f558afe0a66d924e368d174fc3b8098a8f02 100644
--- a/src/ui/viewerStateController/viewerState.useEffect.ts
+++ b/src/state/effects/viewerState.useEffect.ts
@@ -2,26 +2,25 @@ import { Injectable, OnDestroy } from "@angular/core";
 import { Actions, Effect, ofType } from "@ngrx/effects";
 import { Action, select, Store } from "@ngrx/store";
 import { Observable, Subscription, of, merge } from "rxjs";
-import { distinctUntilChanged, filter, map, shareReplay, withLatestFrom, switchMap, mapTo, startWith } from "rxjs/operators";
-import { CHANGE_NAVIGATION, FETCHED_TEMPLATE, IavRootStoreInterface, SELECT_PARCELLATION, SELECT_REGIONS, generalActionError } from "src/services/stateStore.service";
-import { VIEWERSTATE_CONTROLLER_ACTION_TYPES } from "./viewerState.base";
+import { distinctUntilChanged, filter, map, shareReplay, withLatestFrom, switchMap, mapTo, startWith, catchError } from "rxjs/operators";
+import { FETCHED_TEMPLATE, IavRootStoreInterface, SELECT_PARCELLATION, SELECT_REGIONS, generalActionError } from "src/services/stateStore.service";
 import { TemplateCoordinatesTransformation } from "src/services/templateCoordinatesTransformation.service";
 import { CLEAR_STANDALONE_VOLUMES } from "src/services/state/viewerState.store";
 import { viewerStateToggleRegionSelect, viewerStateHelperSelectParcellationWithId, viewerStateSelectTemplateWithId, viewerStateNavigateToRegion, viewerStateSelectedTemplateSelector, viewerStateFetchedTemplatesSelector, viewerStateNewViewer, viewerStateSelectedParcellationSelector, viewerStateNavigationStateSelector, viewerStateSelectTemplateWithName, viewerStateSelectedRegionsSelector, viewerStateSelectAtlas } from "src/services/state/viewerState.store.helper";
 import { ngViewerSelectorClearViewEntries } from "src/services/state/ngViewerState/selectors";
 import { ngViewerActionClearView } from "src/services/state/ngViewerState/actions";
 import { PureContantService } from "src/util";
-import { verifyPositionArg } from 'common/util'
 import { CONST } from 'common/constants'
-import { uiActionHideAllDatasets } from "src/services/state/uiState/actions";
-import { viewerStateFetchedAtlasesSelector } from "src/services/state/viewerState/selectors";
+import { viewerStateFetchedAtlasesSelector, viewerStateGetSelectedAtlas } from "src/services/state/viewerState/selectors";
+import { viewerStateChangeNavigation } from "src/services/state/viewerState/actions";
+import { cvtNavigationObjToNehubaConfig } from 'src/viewerModule/nehuba/util'
 
 const defaultPerspectiveZoom = 1e6
 const defaultZoom = 1e6
 
 export const defaultNavigationObject = {
   orientation: [0, 0, 0, 1],
-  perspectiveOrientation: [0 , 0, 0, 1],
+  perspectiveOrientation: [0.5, -0.5, -0.5, 0.5],
   perspectiveZoom: defaultPerspectiveZoom,
   zoom: defaultZoom,
   position: [0, 0, 0],
@@ -44,7 +43,11 @@ export const defaultNehubaConfigObject = {
 }
 
 export function cvtNehubaConfigToNavigationObj(nehubaConfig?){
-  const { navigation, perspectiveOrientation = [0, 0, 0, 1], perspectiveZoom = 1e6 } = nehubaConfig || {}
+  const {
+    navigation,
+    perspectiveOrientation = defaultNavigationObject.perspectiveOrientation,
+    perspectiveZoom = defaultNavigationObject.perspectiveZoom
+  } = nehubaConfig || {}
   const { pose, zoomFactor = 1e6 } = navigation || {}
   const { position, orientation = [0, 0, 0, 1] } = pose || {}
   const { voxelSize = [1, 1, 1], voxelCoordinates = [0, 0, 0] } = position || {}
@@ -59,44 +62,6 @@ export function cvtNehubaConfigToNavigationObj(nehubaConfig?){
   }
 }
 
-export function cvtNavigationObjToNehubaConfig(navigationObj, nehubaConfigObj){
-  const {
-    orientation = [0, 0, 0, 1],
-    perspectiveOrientation = [0, 0, 0, 1],
-    perspectiveZoom = 1e6,
-    zoom = 1e6,
-    position = [0, 0, 0],
-    positionReal = true,
-  } = navigationObj || {}
-
-  const voxelSize = (() => {
-    const {
-      navigation = {}
-    } = nehubaConfigObj || {}
-    const { pose = {}, zoomFactor = 1e6 } = navigation
-    const { position = {}, orientation = [0, 0, 0, 1] } = pose
-    const { voxelSize = [1, 1, 1], voxelCoordinates = [0, 0, 0] } = position
-    return voxelSize
-  })()
-
-  return {
-    perspectiveOrientation,
-    perspectiveZoom,
-    navigation: {
-      pose: {
-        position: {
-          voxelCoordinates: positionReal
-            ? [0, 1, 2].map(idx => position[idx] / voxelSize[idx])
-            : position,
-          voxelSize
-        },
-        orientation,
-      },
-      zoomFactor: zoom
-    }
-  }
-}
-
 @Injectable({
   providedIn: 'root',
 })
@@ -120,6 +85,10 @@ export class ViewerStateControllerUseEffect implements OnDestroy {
   @Effect()
   public onSelectAtlasSelectTmplParc$ = this.actions$.pipe(
     ofType(viewerStateSelectAtlas.type),
+    switchMap(action => this.pureService.allFetchingReady$.pipe(
+      filter(v => !!v),
+      mapTo(action)
+    )),
     withLatestFrom(
       this.store$.pipe(
         select(viewerStateFetchedTemplatesSelector),
@@ -132,7 +101,8 @@ export class ViewerStateControllerUseEffect implements OnDestroy {
     ),
     map(([action, fetchedTemplates, fetchedAtlases ])=> {
 
-      const atlas = fetchedAtlases.find(a => a['@id'] === (action as any).atlas['@id'])
+      const { atlas: atlasObj } = action as any
+      const atlas = fetchedAtlases.find(a => a['@id'] === atlasObj['@id'])
       if (!atlas) {
         return generalActionError({
           message: CONST.ATLAS_NOT_FOUND
@@ -141,21 +111,34 @@ export class ViewerStateControllerUseEffect implements OnDestroy {
       /**
        * selecting atlas means selecting the first available templateSpace
        */
-      const templateTobeSelected = atlas.templateSpaces[0]
-      const templateSpaceId = templateTobeSelected['@id']
+      const targetTmplSpcId = atlasObj['template']?.['@id']
+      const templateTobeSelected = (
+        targetTmplSpcId
+        && atlas.templateSpaces.find(t => t['@id'] === targetTmplSpcId)
+      ) || atlas.templateSpaces[0]
       
-      const parcellationId = (
-        templateTobeSelected.availableIn.find(p => !!p.baseLayer) ||
-        templateTobeSelected.availableIn[0]
-      )['@id']
-        
+      const templateSpaceId = templateTobeSelected['@id']
+      const atlasTmpl = atlas.templateSpaces.find(t => t['@id'] === templateSpaceId)
+
       const templateSelected = fetchedTemplates.find(t => templateSpaceId === t['@id'])
       if (!templateSelected) {
         return generalActionError({
           message: CONST.TEMPLATE_NOT_FOUND
         })
       }
-      const parcellationSelected = templateSelected.parcellations.find(p => p['@id'] === parcellationId)
+
+      const atlasParcs = atlasTmpl.availableIn
+        .map(availP => atlas.parcellations.find(p => availP['@id'] === p['@id']))
+        .filter(fullP => !!fullP)
+      const atlasParc = atlasParcs.find(p => {
+        if (!p.baseLayer) return false
+        if (p['@version']) {
+          return !p['@version']['@next']
+        }
+        return true
+      }) || templateSelected.parcellations[0]
+      const parcellationId = atlasParc && atlasParc['@id']
+      const parcellationSelected = parcellationId && templateSelected.parcellations.find(p => p['@id'] === parcellationId)
       return viewerStateNewViewer({
         selectTemplate: templateSelected,
         selectParcellation: parcellationSelected
@@ -163,16 +146,6 @@ export class ViewerStateControllerUseEffect implements OnDestroy {
     })
   )
 
-  /**
-   * on region selected change (clear, select, or change selection), clear selected dataset ids
-   */
-  @Effect()
-  public clearShownDatasetIdOnRegionClear: Observable<any> = this.store$.pipe(
-    select(viewerStateSelectedRegionsSelector),
-    mapTo(
-      uiActionHideAllDatasets()
-    )
-  )
 
   @Effect()
   public selectParcellation$: Observable<any>
@@ -385,22 +358,6 @@ export class ViewerStateControllerUseEffect implements OnDestroy {
         map(({ payload }) => payload['@id'])
       ),
 
-      /**
-       * deprecated method...
-       * finding id from name
-       */
-      this.actions$.pipe(
-        ofType(VIEWERSTATE_CONTROLLER_ACTION_TYPES.SELECT_PARCELLATION_WITH_NAME),
-        withLatestFrom(viewerState$.pipe(
-          select('templateSelected')
-        )),
-        map(([ action, templateSelected ]) => {
-          const parcellationName = (action as any).payload.name
-          const foundParcellation = templateSelected.parcellations.find(p => p.name === parcellationName)
-          return foundParcellation && foundParcellation['@id']
-        }),
-        filter(v => !!v)
-      )
     ).pipe(
       withLatestFrom(viewerState$.pipe(
         select('templateSelected'),
@@ -422,35 +379,48 @@ export class ViewerStateControllerUseEffect implements OnDestroy {
 
     this.navigateToRegion$ = this.actions$.pipe(
       ofType(viewerStateNavigateToRegion),
-      map(action => {
-        const { payload = {} } = action as ViewerStateAction
-        const { region } = payload
-        if (!region) {
-          return generalActionError({
-            message: `Go to region: region not defined`
-          })
-        }
-
-        const { position } = region
-        if (!position) {
-          return generalActionError({
-            message: `${region.name} - does not have a position defined`
-          })
-        }
-
-        if (!verifyPositionArg(position)){
-          return generalActionError({
-            message: `${region.name} has malformed position property: ${JSON.stringify(position)}`
-          })
-        }
-
-        return {
-          type: CHANGE_NAVIGATION,
-          navigation: {
-            position,
-            animation: {},
-          },
+      map(action => action.payload?.region),
+      withLatestFrom(
+        this.store$.pipe(
+          select(viewerStateGetSelectedAtlas)
+        ),
+        this.store$.pipe(
+          select(viewerStateSelectedTemplateSelector)
+        ),
+        this.store$.pipe(
+          select(viewerStateSelectedParcellationSelector)
+        )
+      ),
+      switchMap(([ region,  selectedAtlas, selectedTemplate, selectedParcellation ]) => {
+        if (!region || !selectedAtlas || !selectedTemplate || !selectedParcellation) {
+          return of(
+            generalActionError({
+              message: `Go to region: region / atlas / template / parcellation not defined.`
+            })
+          )
         }
+        return this.pureService.getRegionDetail(selectedAtlas['@id'], selectedParcellation['@id'], selectedTemplate['@id'], region).pipe(
+          map(regDetail => {
+            const pos = (() => {
+              if (!regDetail) throw new Error(`region detail not found!`)
+              if (!regDetail.props) throw new Error(`region does not have props defined!`)
+              if (!regDetail.props.length) throw new Error(`region props not found!`)
+              return regDetail.props[0].centroid_mm
+            })()
+            
+            return viewerStateChangeNavigation({
+              navigation: {
+                position: pos.map(v => v * 1e6),
+                animation: {},
+              }
+            })
+          }),
+          catchError((err) => of(
+            generalActionError({
+              message: `Fetching region detail error: ${err}`
+            })
+          ))
+        )
       }),
     )
 
diff --git a/src/state/index.ts b/src/state/index.ts
index d5efade12f4641c39c4a808d81c7eb7e531ba31b..778709a1c7cdd5a85c71824a18b8c2f1aff1df07 100644
--- a/src/state/index.ts
+++ b/src/state/index.ts
@@ -1 +1,5 @@
-export { StateModule } from './state.module'
+export { StateModule } from "./state.module"
+export {
+  ViewerStateControllerUseEffect,
+  cvtNehubaConfigToNavigationObj,
+} from "./effects/viewerState.useEffect"
\ No newline at end of file
diff --git a/src/state/stateAggregator.directive.ts b/src/state/stateAggregator.directive.ts
index c9fd436c3b8ee3370b4df72acee3174c18769fae..62d66666227e91a18bd1aac9ba2309d7a5c43a8d 100644
--- a/src/state/stateAggregator.directive.ts
+++ b/src/state/stateAggregator.directive.ts
@@ -1,11 +1,10 @@
 import { Directive } from "@angular/core";
-import { Store } from "@ngrx/store";
+import { NavigationEnd, Router } from "@angular/router";
 import { Observable } from "rxjs";
-import { map, debounceTime, shareReplay } from "rxjs/operators";
-import { IavRootStoreInterface } from "src/services/stateStore.service";
-import { cvtStateToSearchParam } from "src/atlasViewer/atlasViewer.urlUtil";
+import { filter, map } from "rxjs/operators";
 
-const jsonVersion = '0.0.1'
+const jsonVersion = '1.0.0'
+// ver 0.0.1 === query param
 
 interface IJsonifiedState {
   ver: string
@@ -21,18 +20,16 @@ export class StateAggregator{
 
   public jsonifiedSstate$: Observable<IJsonifiedState>
   constructor(
-    private store$: Store<IavRootStoreInterface>
+    router: Router
   ){
-    this.jsonifiedSstate$ = this.store$.pipe(
-      debounceTime(100),
-      map(json => {
-        const queryString = cvtStateToSearchParam(json)
+    this.jsonifiedSstate$ = router.events.pipe(
+      filter(ev => ev instanceof NavigationEnd),
+      map((ev: NavigationEnd) => {
         return {
           ver: jsonVersion,
-          queryString: queryString.toString()
+          queryString: ev.urlAfterRedirects
         }
-      }),
-      shareReplay(1)
+      })
     )
   }
 }
diff --git a/src/theme.scss b/src/theme.scss
index b2b1c43ca50117e48bfc37e5b27ddbf507686d6b..f311da58eb993b86a235179246c4ab88dcbc79c1 100644
--- a/src/theme.scss
+++ b/src/theme.scss
@@ -1,4 +1,5 @@
 @import '~@angular/material/theming';
+@import '~@angular/cdk/overlay-prebuilt.css';
 
 @include  mat-core();
 
@@ -39,7 +40,7 @@
     &[bg],
     &.bg
     {
-      background-color: mat-color($background, background)
+      background-color: mat-color($background, background);
     }
 
     &[darker-bg],
@@ -70,6 +71,15 @@
     {
       color: mat-color($warn);
     }
+
+    &.hoverable
+    {
+      &:hover
+      {
+        background-color: mat-color($background, hover);
+        cursor: pointer;
+      }
+    }
   }
 }
 
diff --git a/src/ui/atlasLayerSelector/atlasLayerSelector.component.ts b/src/ui/atlasLayerSelector/atlasLayerSelector.component.ts
deleted file mode 100644
index 5f98a274ce9cbf0b957dccdc2a66ad7d9dc6556b..0000000000000000000000000000000000000000
--- a/src/ui/atlasLayerSelector/atlasLayerSelector.component.ts
+++ /dev/null
@@ -1,215 +0,0 @@
-import { Component, OnInit, ViewChildren, QueryList, HostBinding } from "@angular/core";
-import { select, Store } from "@ngrx/store";
-import { distinctUntilChanged, map, withLatestFrom, shareReplay, groupBy, mergeMap, toArray, switchMap, scan, filter, startWith } from "rxjs/operators";
-import { Observable, Subscription, from, zip, of, combineLatest } from "rxjs";
-import { viewerStateSelectTemplateWithId, viewerStateToggleLayer } from "src/services/state/viewerState.store.helper";
-import { MatMenuTrigger } from "@angular/material/menu";
-import { viewerStateGetSelectedAtlas, viewerStateAtlasLatestParcellationSelector, viewerStateSelectedTemplateFullInfoSelector, viewerStateSelectedTemplatePureSelector, viewerStateSelectedParcellationSelector } from "src/services/state/viewerState/selectors";
-import { ARIA_LABELS } from 'common/constants'
-
-@Component({
-  selector: 'atlas-layer-selector',
-  templateUrl: './atlasLayerSelector.template.html',
-  styleUrls: ['./atlasLayerSelector.style.css'],
-  exportAs: 'atlasLayerSelector'
-})
-export class AtlasLayerSelector implements OnInit {
-
-    public TOGGLE_ATLAS_LAYER_SELECTOR = ARIA_LABELS.TOGGLE_ATLAS_LAYER_SELECTOR
-
-    @ViewChildren(MatMenuTrigger) matMenuTriggers: QueryList<MatMenuTrigger>
-    public atlas: any
-
-    public nonGroupedLayers$: Observable<any[]>
-    public groupedLayers$: Observable<any[]>
-
-    public selectedTemplateSpaceId: string
-    public selectedLayers = []
-
-    public selectedTemplate$: Observable<any>
-    private selectedParcellation$: Observable<any>
-    public selectedAtlas$: Observable<any>
-    private subscriptions: Subscription[] = []
-
-    @HostBinding('attr.data-opened')
-    public selectorExpanded: boolean = false
-    public selectedTemplatePreviewUrl: string = ''
-
-    public availableTemplates$: Observable<any[]>
-
-    public containerMaxWidth: number
-
-    constructor(private store$: Store<any>) {
-      this.selectedAtlas$ = this.store$.pipe(
-        select(viewerStateGetSelectedAtlas),
-        distinctUntilChanged(),
-        shareReplay(1)
-      )
-
-      this.availableTemplates$ = this.store$.pipe(
-        select(viewerStateSelectedTemplateFullInfoSelector),
-        map(v => v || []),
-        startWith<any[]>([])
-      )
-
-      this.selectedTemplate$ = this.store$.pipe(
-        select(viewerStateSelectedTemplatePureSelector),
-        withLatestFrom(this.selectedAtlas$),
-        map(([templateSelected, templateFromAtlas]) => {
-          return {
-            ...templateFromAtlas,
-            ...templateSelected
-          }
-        })
-      )
-      this.selectedParcellation$ = this.store$.pipe(
-        select(viewerStateSelectedParcellationSelector)
-      )
-
-      const layersGroupBy$ = this.selectedAtlas$.pipe(
-        switchMap(selectedAtlas => from((selectedAtlas?.parcellations) || []).pipe(
-          /**
-           * do not show base layers
-           */
-          filter(p => !(p as any).baseLayer),
-          groupBy((parcellation: any) => parcellation.groupName, p => p),
-          mergeMap(group => zip(
-            of(group.key),
-            group.pipe(toArray()))
-          ),
-          scan((acc, curr) => acc.concat([ curr ]), []),
-          shareReplay(1),
-        ))
-      )
-
-      const atlasLayersLatest$ = this.store$.pipe(
-        select(viewerStateAtlasLatestParcellationSelector),
-        shareReplay(1),
-      )
-
-      this.nonGroupedLayers$ = atlasLayersLatest$.pipe(
-        map(allParcellations => 
-          allParcellations
-            .filter(p => !p['groupName'])
-            .filter(p => !p['baseLayer'])
-        ),
-        startWith<any[]>([])
-      )
-
-      this.groupedLayers$ = combineLatest([
-        atlasLayersLatest$.pipe(
-          map(allParcellations => 
-            allParcellations.filter(p => !p['baseLayer'])
-          ),
-        ),
-        layersGroupBy$
-      ]).pipe(
-        map(([ allParcellations, arr]) => arr
-          .filter(([ key ]) => !!key )
-          .map(([key, parcellations]) => ({
-            name: key,
-            previewUrl: parcellations[0].previewUrl,
-            parcellations: parcellations.map(p => {
-              const fullInfo = allParcellations.find(fullP => fullP['@id'] === p['@id']) || {}
-              return {
-                ...fullInfo,
-                ...p,
-                darktheme: (fullInfo || {}).useTheme === 'dark'
-              }
-            })
-          }))
-        ),
-        startWith<any[]>([])
-      )
-    }
-
-    ngOnInit(): void {
-      this.subscriptions.push(
-        this.selectedTemplate$.subscribe(st => {
-          this.selectedTemplatePreviewUrl = st.templateSpaces?.find(t => t['@id'] === st['@id']).previewUrl
-          this.selectedTemplateSpaceId = st['@id']
-        }),
-      )
-      this.subscriptions.push(
-        this.selectedParcellation$.subscribe(ps => {
-          this.selectedLayers = (this.atlas && [this.atlas.parcellations.find(l => l['@id'] === ps['@id'])['@id']]) || []
-        })
-      )
-      this.subscriptions.push(
-        this.selectedAtlas$.subscribe(sa => {
-          this.atlas = sa
-        })
-      )
-    }
-
-    selectTemplateWithName(template) {
-      this.store$.dispatch(
-        viewerStateSelectTemplateWithId({ payload: template })
-      )
-    }
-
-    selectParcellationWithName(layer) {
-      const templateChangeRequired = !this.currentTemplateIncludesLayer(layer)
-      if (!templateChangeRequired) {
-        this.store$.dispatch(
-          viewerStateToggleLayer({ payload: layer })
-        )
-      } else {
-        this.store$.dispatch(
-          viewerStateSelectTemplateWithId({ payload: layer.availableIn[0], config: { selectParcellation: layer } })
-        )
-      }
-    }
-
-    currentTemplateIncludesLayer(layer) {
-      return layer && layer.availableIn.map(a => a['@id']).includes(this.selectedTemplateSpaceId)
-    }
-
-    templateIncludesGroup(group) {
-      return group.parcellations.some(v => v.availableIn.map(t => t['@id']).includes(this.selectedTemplateSpaceId))
-    }
-
-    selectedOneOfTheLayers(layers) {
-      const includes = layers.map(l=>l['@id']).some(id=> this.selectedLayers.includes(id))
-      return includes
-    }
-
-    selectedLayersIncludes(id) {
-      return this.selectedLayers.includes(id)
-    }
-
-    collapseExpandedGroup(){
-      this.matMenuTriggers.forEach(t => t.menuOpen && t.closeMenu())
-    }
-
-    getTileTmplClickFnAsCtx(fn: (...arg) => void, param: any) {
-      return () => fn.call(this, param)
-    }
-
-    getTooltipText(layer) {
-      if (this.atlas.templateSpaces.map(tmpl => tmpl['@id']).includes(layer['@id'])) return layer.name
-      if (layer.availableIn) {
-        if (this.currentTemplateIncludesLayer(layer)) return layer.name
-        else {
-          const firstAvailableRefSpaceId = layer && Array.isArray(layer.availableIn) && layer.availableIn.length > 0 && layer.availableIn[0]['@id']
-          const firstAvailableRefSpace = firstAvailableRefSpaceId && this.atlas.templateSpaces.find(t => t['@id'] === firstAvailableRefSpaceId)
-          return `${layer.name} 🔄 ${(firstAvailableRefSpace && firstAvailableRefSpace.name) || ''}`
-        }
-      }
-
-      if (layer.parcellations) {
-        if (this.templateIncludesGroup(layer)) return layer.name
-        else return `${layer.name} 🔄`
-      }
-      
-      return layer.name
-    }
-
-    trackbyAtId(t){
-      return t['@id']
-    }
-
-    trackbyName(t) {
-      return t['name']
-    }
-}
diff --git a/src/ui/atlasLayerSelector/atlasLayerSelector.style.css b/src/ui/atlasLayerSelector/atlasLayerSelector.style.css
deleted file mode 100644
index 4e20ddc781223acdeb53989f89b946ed306d8194..0000000000000000000000000000000000000000
--- a/src/ui/atlasLayerSelector/atlasLayerSelector.style.css
+++ /dev/null
@@ -1,134 +0,0 @@
-.singleLayerImageContainer img {
-    flex-shrink: 0;
-    /*min-width: 100%;*/
-    /*min-height: 100%;*/
-    width: 70px;
-    height: 70px;
-    border-radius: 10px;
-    -webkit-border-radius: 10px;
-    -moz-border-radius: 10px;
-}
-
-
-.selectedTemplateDefaultContainer {
-    width: 100px;
-    height: 100px;
-    border-radius: 10px;
-}
-.selectedTemplateDefaultContainer img {
-    border-radius: 10px;
-    min-width: 100%;
-    min-height: 100%;
-    width: 100%;
-}
-
-.selectedLayerBorder {
-    border: 2px solid #FED363;
-}
-
-.scale-up-bl {
-    -webkit-animation: scale-up-bl .2s cubic-bezier(.39, .575, .565, 1.000) both;
-    animation: scale-up-bl .2s cubic-bezier(.39, .575, .565, 1.000) both;
-}
-
-@-webkit-keyframes scale-up-bl {
-    0% {
-        -webkit-transform: scale(.5);
-        transform: scale(.5);
-        -webkit-transform-origin: 0 100%;
-        transform-origin: 0 100%;
-    }
-    100% {
-        -webkit-transform: scale(1);
-        transform: scale(1);
-        -webkit-transform-origin: 0 100%;
-        transform-origin: 0 100%;
-    }
-}
-
-@keyframes scale-up-bl {
-    0% {
-        -webkit-transform: scale(.5);
-        transform: scale(.5);
-        -webkit-transform-origin: 0 100%;
-        transform-origin: 0 100%;
-    }
-    100% {
-        -webkit-transform: scale(1);
-        transform: scale(1);
-        -webkit-transform-origin: 0 100%;
-        transform-origin: 0 100%;
-    }
-}
-
-
-.scale-down-bl {
-    -webkit-animation: scale-down-bl .2s cubic-bezier(.25, .46, .45, .94) both;
-    animation: scale-down-bl .2s cubic-bezier(.25, .46, .45, .94) both;
-}
-
-@-webkit-keyframes scale-down-bl {
-    0% {
-        -webkit-transform: scale(1);
-        transform: scale(1);
-        -webkit-transform-origin: 0 100%;
-        transform-origin: 0 100%;
-        opacity: 1;
-    }
-    100% {
-        -webkit-transform: scale(.5);
-        transform: scale(.5);
-        -webkit-transform-origin: 0 100%;
-        transform-origin: 0 100%;
-        opacity: 0;
-    }
-}
-
-@keyframes scale-down-bl {
-    0% {
-        -webkit-transform: scale(1);
-        transform: scale(1);
-        -webkit-transform-origin: 0 100%;
-        transform-origin: 0 100%;
-        opacity: 1;
-    }
-    100% {
-        -webkit-transform: scale(.5);
-        transform: scale(.5);
-        -webkit-transform-origin: 0 100%;
-        transform-origin: 0 100%;
-        opacity: 0;
-    }
-}
-
-.folder-container
-{
-    margin-right:0.5rem;
-    margin-bottom:-0.5rem;
-}
-
-.info-container
-{
-    margin-right:-0.5rem;
-    margin-top:-0.25rem;
-}
-
-.selector-container
-{
-    width: 21rem;
-    bottom: 4rem;
-    z-index: 5;
-}
-
-.single-column-tile
-{
-    width: calc((28rem - 32px)/3);
-}
-
-.infoButton {
-    height: 18px;
-    width: 18px;
-    margin-top: 10px;
-    margin-right: 12px;
-    border-radius: 50%;
-}
diff --git a/src/ui/btnShadow.style.css b/src/ui/btnShadow.style.css
deleted file mode 100644
index 3d4bb9c2dd3ef38fcaa74e5deb5406132ffbde03..0000000000000000000000000000000000000000
--- a/src/ui/btnShadow.style.css
+++ /dev/null
@@ -1,23 +0,0 @@
-.btnWrapper
-{
-  display:flex;
-  align-items: center;
-  justify-content: center;
-}
-
-
-.btnWrapper > .btn
-{
-  width: 2.5em;
-  height: 2.5em;
-
-  display: flex;
-  align-items: center;
-  justify-content: center;
-}
-
-.btnWrapper.btnWrapper-lg > .btn
-{
-  width: 5rem;
-  height: 5rem;
-}
\ No newline at end of file
diff --git a/src/ui/config/config.component.ts b/src/ui/config/configCmp/config.component.ts
similarity index 98%
rename from src/ui/config/config.component.ts
rename to src/ui/config/configCmp/config.component.ts
index 4127b3bf990e732f1fccd4d94b69a5a0306d18f2..15f92277c707e22aeeb25cb64dec3e562f907030 100644
--- a/src/ui/config/config.component.ts
+++ b/src/ui/config/configCmp/config.component.ts
@@ -6,7 +6,7 @@ import { SUPPORTED_PANEL_MODES } from 'src/services/state/ngViewerState.store';
 import { ngViewerActionSetPanelOrder } from 'src/services/state/ngViewerState.store.helper';
 import { VIEWER_CONFIG_ACTION_TYPES, StateInterface as ViewerConfiguration } from 'src/services/state/viewerConfig.store'
 import { IavRootStoreInterface } from 'src/services/stateStore.service';
-import { isIdentityQuat } from '../nehubaContainer/util';
+import { isIdentityQuat } from 'src/viewerModule/nehuba/util';
 import {MatSlideToggleChange} from "@angular/material/slide-toggle";
 import {MatSliderChange} from "@angular/material/slider";
 import { PureContantService } from 'src/util';
diff --git a/src/ui/config/config.style.css b/src/ui/config/configCmp/config.style.css
similarity index 100%
rename from src/ui/config/config.style.css
rename to src/ui/config/configCmp/config.style.css
diff --git a/src/ui/config/config.template.html b/src/ui/config/configCmp/config.template.html
similarity index 98%
rename from src/ui/config/config.template.html
rename to src/ui/config/configCmp/config.template.html
index adfe40f939b86f9a8ccf7884b49ec0f426a524aa..deb625baac125ea4ff0a47915adf349144d31ae1 100644
--- a/src/ui/config/config.template.html
+++ b/src/ui/config/configCmp/config.template.html
@@ -201,5 +201,10 @@
       </div>
     </div>
   </mat-tab>
+
+  <!-- plugin csp -->
+  <mat-tab label="Plugin Permission">
+    <plugin-csp-controller></plugin-csp-controller>
+  </mat-tab>
 </mat-tab-group>
 
diff --git a/src/ui/config/index.ts b/src/ui/config/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/ui/config/module.ts b/src/ui/config/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..34ed41cb182f81d22a42905f06a6ceddddf462f0
--- /dev/null
+++ b/src/ui/config/module.ts
@@ -0,0 +1,22 @@
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { LayoutModule } from "src/layouts/layout.module";
+import { PluginModule } from "src/plugin";
+import { AngularMaterialModule } from "../sharedModules/angularMaterial.module";
+import { ConfigComponent } from "./configCmp/config.component";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    AngularMaterialModule,
+    PluginModule,
+    LayoutModule,
+  ],
+  declarations: [
+    ConfigComponent,
+  ],
+  exports: [
+    ConfigComponent,
+  ]
+})
+export class ConfigModule{}
\ No newline at end of file
diff --git a/src/ui/connectivityBrowser/connectivityBrowser.component.spec.ts b/src/ui/connectivityBrowser/connectivityBrowser.component.spec.ts
deleted file mode 100644
index 72aa760cb486d4a0457df4669ea12559ec446ee3..0000000000000000000000000000000000000000
--- a/src/ui/connectivityBrowser/connectivityBrowser.component.spec.ts
+++ /dev/null
@@ -1,78 +0,0 @@
-import {ConnectivityBrowserComponent} from "src/ui/connectivityBrowser/connectivityBrowser.component";
-import {async, ComponentFixture, TestBed} from "@angular/core/testing";
-import {Action} from "@ngrx/store";
-import {
-    defaultRootState,
-} from "src/services/stateStore.service";
-import {HttpClientModule} from "@angular/common/http";
-import {CUSTOM_ELEMENTS_SCHEMA} from "@angular/core";
-import {DatabrowserModule} from "src/ui/databrowserModule";
-import {provideMockActions} from "@ngrx/effects/testing";
-import {provideMockStore} from "@ngrx/store/testing";
-import {Observable, of} from "rxjs";
-
-describe('ConnectivityComponent', () => {
-
-    let component: ConnectivityBrowserComponent;
-    let fixture: ComponentFixture<ConnectivityBrowserComponent>;
-    const actions$: Observable<Action> = of({type: 'TEST'})
-
-    let datasetList = [
-        {
-            id: 'id1',
-            name: 'n1',
-            description: 'd1',
-            kgId: 'kgId1',
-            kgschema: 'kgschema1'
-        }, {
-            id: 'id2',
-            name: 'n2',
-            description: 'd2',
-            kgId: 'kgId2',
-            kgschema: 'kgschema2'
-        }
-    ]
-
-    beforeEach(async (() => {
-        TestBed.configureTestingModule({
-            imports: [
-                HttpClientModule,
-                DatabrowserModule
-            ],
-            providers: [
-                provideMockActions(() => actions$),
-                provideMockStore({ initialState: defaultRootState })
-            ],
-            declarations: [ConnectivityBrowserComponent],
-            schemas: [
-                CUSTOM_ELEMENTS_SCHEMA,
-            ],
-        }).compileComponents()
-    }));
-
-    it('> component can be created', async () => {
-        fixture = TestBed.createComponent(ConnectivityBrowserComponent)
-        component = fixture.componentInstance
-        expect(component).toBeTruthy()
-    })
-
-    it('> change dataset changes description, kgId and kgschema', () => {
-        fixture = TestBed.createComponent(ConnectivityBrowserComponent)
-        component = fixture.componentInstance
-
-        component.datasetList = datasetList
-
-        component.changeDataset({value: 'n1'})
-
-        expect(component.selectedDatasetDescription).toEqual('d1')
-        expect(component.selectedDatasetKgId).toEqual('kgId1')
-        expect(component.selectedDatasetKgSchema).toEqual('kgschema1')
-
-        component.changeDataset({value: 'n2'})
-
-        expect(component.selectedDatasetDescription).toEqual('d2')
-        expect(component.selectedDatasetKgId).toEqual('kgId2')
-        expect(component.selectedDatasetKgSchema).toEqual('kgschema2')
-    })
-
-});
\ No newline at end of file
diff --git a/src/ui/cookieAgreement/cookieAgreement.component.ts b/src/ui/cookieAgreement/cookieAgreement/cookieAgreement.component.ts
similarity index 81%
rename from src/ui/cookieAgreement/cookieAgreement.component.ts
rename to src/ui/cookieAgreement/cookieAgreement/cookieAgreement.component.ts
index 55f114b49121f2f7bc8c7e8d879a672346eaf4f8..fe1643a45e2a5152199d7aea5211695183b44310 100644
--- a/src/ui/cookieAgreement/cookieAgreement.component.ts
+++ b/src/ui/cookieAgreement/cookieAgreement/cookieAgreement.component.ts
@@ -1,7 +1,7 @@
 import { ChangeDetectionStrategy, Component } from '@angular/core'
-import info from '!!raw-loader!./data/info.md'
-import readmore from '!!raw-loader!./data/readmore.md'
-import matomoInfo from '!!raw-loader!./data/aboutMatomo.md'
+import info from '!!raw-loader!../data/info.md'
+import readmore from '!!raw-loader!../data/readmore.md'
+import matomoInfo from '!!raw-loader!../data/aboutMatomo.md'
 
 @Component({
   selector: 'cookie-agreement',
diff --git a/src/ui/cookieAgreement/cookieAgreement.style.css b/src/ui/cookieAgreement/cookieAgreement/cookieAgreement.style.css
similarity index 100%
rename from src/ui/cookieAgreement/cookieAgreement.style.css
rename to src/ui/cookieAgreement/cookieAgreement/cookieAgreement.style.css
diff --git a/src/ui/cookieAgreement/cookieAgreement.template.html b/src/ui/cookieAgreement/cookieAgreement/cookieAgreement.template.html
similarity index 100%
rename from src/ui/cookieAgreement/cookieAgreement.template.html
rename to src/ui/cookieAgreement/cookieAgreement/cookieAgreement.template.html
diff --git a/src/ui/cookieAgreement/index.ts b/src/ui/cookieAgreement/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/ui/cookieAgreement/module.ts b/src/ui/cookieAgreement/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8ee42e1697f8f932fc6545c8a010bbe5c5cda853
--- /dev/null
+++ b/src/ui/cookieAgreement/module.ts
@@ -0,0 +1,23 @@
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { ComponentsModule } from "src/components";
+import { UtilModule } from "src/util";
+import { AngularMaterialModule } from "../sharedModules/angularMaterial.module";
+import { CookieAgreement } from "./cookieAgreement/cookieAgreement.component";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    AngularMaterialModule,
+    UtilModule,
+    ComponentsModule,
+  ],
+  declarations: [
+    CookieAgreement,
+  ],
+  exports: [
+    CookieAgreement
+  ]
+})
+
+export class CookieModule{}
\ No newline at end of file
diff --git a/src/ui/databrowserModule/databrowser/databrowser.base.ts b/src/ui/databrowserModule/databrowser/databrowser.base.ts
deleted file mode 100644
index c2c8279a94419ca203a44a2a011aa6c9c5b098d3..0000000000000000000000000000000000000000
--- a/src/ui/databrowserModule/databrowser/databrowser.base.ts
+++ /dev/null
@@ -1,84 +0,0 @@
-import { Input, Output, EventEmitter, OnDestroy } from "@angular/core"
-import { LoggingService } from "src/logging"
-import { DatabrowserService } from "../singleDataset/singleDataset.base"
-import { Observable, Subject, Subscription } from "rxjs"
-import { IDataEntry } from "src/services/stateStore.service"
-import { setsEql } from 'common/util'
-import { switchMap, tap } from "rxjs/operators"
-import { getStringIdsFromRegion, flattenReducer } from 'common/util'
-
-export class DatabrowserBase implements OnDestroy{
-
-  private _subscriptions: Subscription[] = []
-
-  @Output()
-  public dataentriesUpdated: EventEmitter<IDataEntry[]> = new EventEmitter()
-
-  private _regions: any[] = []
-  
-  public regions$ = new Subject<any[]>()
-  get regions(){
-    return this._regions
-  }
-  @Input()
-  set regions(arr: any[]){
-    const currentSet = new Set(this._regions.map(r => getStringIdsFromRegion(r)).reduce(flattenReducer, []))
-    const newSet = new Set(arr.map(r => getStringIdsFromRegion(r)).reduce(flattenReducer, []).filter(v => !!v))
-    if (setsEql(newSet, currentSet)) return
-    this._regions = arr.filter(r => !getStringIdsFromRegion(r).every(id => !id))
-    this.regions$.next(this._regions)
-  }
-
-  public fetchError: boolean = false
-  public fetchingFlag = false
-
-  public favDataentries$: Observable<Partial<IDataEntry>[]>
-
-  public dataentries: IDataEntry[] = []
-
-  constructor(
-    private dbService: DatabrowserService,
-    private log: LoggingService,
-  ){
-
-    this.favDataentries$ = this.dbService.favedDataentries$
-    
-    this._subscriptions.push(
-      this.regions$.pipe(
-        tap(() => this.fetchingFlag = true),
-        switchMap(regions => this.dbService.getDataByRegion({ regions })),
-      ).subscribe(
-        de => {
-          this.fetchingFlag = false
-          this.dataentries = de
-          this.dataentriesUpdated.emit(de)
-        },
-        e => {
-          this.log.error(e)
-          this.fetchError = true
-        }
-      )
-    )
-  }
-
-  ngOnDestroy(){
-    while(this._subscriptions.length > 0) this._subscriptions.pop().unsubscribe()
-  }
-
-  public retryFetchData(event: MouseEvent) {
-    event.preventDefault()
-    this.dbService.manualFetchDataset$.next(null)
-  }
-
-  public toggleFavourite(dataset: IDataEntry) {
-    this.dbService.toggleFav(dataset)
-  }
-
-  public saveToFavourite(dataset: IDataEntry) {
-    this.dbService.saveToFav(dataset)
-  }
-
-  public removeFromFavourite(dataset: IDataEntry) {
-    this.dbService.removeFromFav(dataset)
-  }
-}
diff --git a/src/ui/databrowserModule/databrowser/databrowser.component.ts b/src/ui/databrowserModule/databrowser/databrowser.component.ts
deleted file mode 100644
index 59b813972f72d3b3fdd5fb36b248ab5eaba6049e..0000000000000000000000000000000000000000
--- a/src/ui/databrowserModule/databrowser/databrowser.component.ts
+++ /dev/null
@@ -1,155 +0,0 @@
-import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit, ViewChild } from "@angular/core";
-import { Subscription } from "rxjs";
-import { LoggingService } from "src/logging";
-import { IDataEntry } from "src/services/state/dataStore.store";
-import { CountedDataModality, DatabrowserService } from "../databrowser.service";
-import { ModalityPicker } from "../modalityPicker/modalityPicker.component";
-import { ARIA_LABELS } from 'common/constants.js'
-import { DatabrowserBase } from "./databrowser.base";
-import { debounceTime } from "rxjs/operators";
-import { OVERWRITE_SHOW_DATASET_DIALOG_TOKEN, TOverwriteShowDatasetDialog } from "src/util/interfaces";
-import { Store } from "@ngrx/store";
-import { uiActionShowDatasetWtihId } from "src/services/state/uiState/actions";
-
-const { MODALITY_FILTER, LIST_OF_DATASETS } = ARIA_LABELS
-
-@Component({
-  selector : 'data-browser',
-  templateUrl : './databrowser.template.html',
-  styleUrls : [
-    `./databrowser.style.css`,
-  ],
-  exportAs: 'dataBrowser',
-  changeDetection: ChangeDetectionStrategy.OnPush,
-  providers: [
-
-    {
-      provide: OVERWRITE_SHOW_DATASET_DIALOG_TOKEN,
-      useFactory: (store: Store<any>) => {
-        return function overwriteShowDatasetDialog( arg: { fullId?: string, name: string, description: string } ){
-          if (arg.fullId) {
-            store.dispatch(
-              uiActionShowDatasetWtihId({
-                id: arg.fullId
-              })
-            )
-          }
-        } as TOverwriteShowDatasetDialog
-      },
-      deps: [
-        Store
-      ]
-    }
-  ]
-  
-})
-
-export class DataBrowser extends DatabrowserBase implements OnDestroy, OnInit {
-
-  @Input()
-  disableVirtualScroll: boolean = false
-
-  @Input()
-  showList: boolean = true
-
-  public MODALITY_FILTER_ARIA_LABEL = MODALITY_FILTER
-  public LIST_OF_DATASETS_ARIA_LABEL = LIST_OF_DATASETS
-
-
-  /**
-   * TODO filter types
-   */
-  private subscriptions: Subscription[] = []
-  public countedDataM: CountedDataModality[] = []
-  public visibleCountedDataM: CountedDataModality[] = []
-
-  @ViewChild(ModalityPicker)
-  public modalityPicker: ModalityPicker
-
-
-  /**
-   * TODO
-   * viewport
-   * user defined filter
-   * etc
-   */
-  public gemoetryFilter: any
-
-  constructor(
-    private dataService: DatabrowserService,
-    private cdr: ChangeDetectorRef,
-    log: LoggingService,
-  ) {
-    super(dataService, log)
-  }
-
-  public ngOnInit() {
-
-    /**
-     * in the event that dataentries are updated before ngInit lifecycle hook
-     */
-    this.countedDataM = this.dataService.getModalityFromDE(this.dataentries)
-
-    this.subscriptions.push(
-      this.dataentriesUpdated.pipe(
-        debounceTime(60)
-      ).subscribe(() => {
-        this.countedDataM = this.dataService.getModalityFromDE(this.dataentries)
-        this.cdr.markForCheck()
-      })
-    )
-    
-    /**
-     * TODO gets init'ed everytime when appends to ngtemplateoutlet
-     */
-    this.dataService.dbComponentInit(this)
-
-    /**
-     * TODO fix
-     */
-    // this.subscriptions.push(
-    //   this.filterApplied$.subscribe(() => this.currentPage = 0)
-    // )
-  }
-
-  public ngOnDestroy() {
-    super.ngOnDestroy()
-    this.subscriptions.forEach(s => s.unsubscribe())
-  }
-
-  public clearAll() {
-    this.countedDataM = this.countedDataM.map(cdm => {
-      return {
-        ...cdm,
-        visible: false,
-      }
-    })
-    this.visibleCountedDataM = []
-  }
-
-  public handleModalityFilterEvent(modalityFilter: CountedDataModality[]) {
-    this.countedDataM = modalityFilter
-    this.visibleCountedDataM = modalityFilter.filter(dm => dm.visible)
-    this.cdr.markForCheck()
-  }
-
-  public showParcellationList: boolean = false
-
-  public filePreviewName: string
-  public onShowPreviewDataset(payload: {datasetName: string, event: MouseEvent}) {
-    const { datasetName } = payload
-    this.filePreviewName = datasetName
-  }
-
-  public resetFilters(_event?: MouseEvent) {
-    this.clearAll()
-  }
-
-  public trackByFn(index: number, dataset: IDataEntry) {
-    return dataset.id
-  }
-}
-
-export interface IDataEntryFilter {
-  filter: (dataentries: IDataEntry[]) => IDataEntry[]
-}
diff --git a/src/ui/databrowserModule/databrowser/databrowser.directive.ts b/src/ui/databrowserModule/databrowser/databrowser.directive.ts
deleted file mode 100644
index 911a8a2b9497f821150c125cc12006cb8e2f2d7e..0000000000000000000000000000000000000000
--- a/src/ui/databrowserModule/databrowser/databrowser.directive.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import { Directive, OnDestroy } from "@angular/core";
-import { DatabrowserBase } from "./databrowser.base";
-import { DatabrowserService } from "../singleDataset/singleDataset.base";
-import { LoggingService } from "src/logging";
-
-@Directive({
-  selector: '[iav-databrowser-directive]',
-  exportAs: 'iavDatabrowserDirective'
-})
-
-export class DatabrowserDirective extends DatabrowserBase implements OnDestroy{
-  constructor(
-    dataService: DatabrowserService,
-    log: LoggingService,
-  ){
-    super(dataService, log)
-  }
-
-  ngOnDestroy(){
-    super.ngOnDestroy()
-  }
-}
diff --git a/src/ui/databrowserModule/databrowser/databrowser.style.css b/src/ui/databrowserModule/databrowser/databrowser.style.css
deleted file mode 100644
index 0320f94c00443a8ec0a67f97b4aa1ea7d9842a09..0000000000000000000000000000000000000000
--- a/src/ui/databrowserModule/databrowser/databrowser.style.css
+++ /dev/null
@@ -1,24 +0,0 @@
-:host
-{
-  display: flex;
-  flex-direction: column;
-  width: 100%;
-  height: 100%;
-}
-
-modality-picker
-{
-  font-size: 90%;
-}
-
-radio-list
-{
-  display: block;
-}
-
-
-/* grow 3x shrink 1x basis 2.5 x 50px (2.5 rows) */
-.dataset-container
-{
-  flex: 3 1 125px;
-}
diff --git a/src/ui/databrowserModule/databrowser/databrowser.template.html b/src/ui/databrowserModule/databrowser/databrowser.template.html
deleted file mode 100644
index 7c684c6cdf0fbe4332c4488c551b4caf63c98e17..0000000000000000000000000000000000000000
--- a/src/ui/databrowserModule/databrowser/databrowser.template.html
+++ /dev/null
@@ -1,173 +0,0 @@
-<!-- transclusion header -->
-<ng-content select="[card-header]">
-</ng-content>
-
-<!-- transclusion content prepend -->
-<ng-content select="[card-content='prepend']">
-</ng-content>
-
-<!-- modality filter -->
-<div class="mb-1" >
-  <ng-container *ngTemplateOutlet="modalitySelector">
-  </ng-container>
-</div>
-
-<!-- if still loading, show spinner -->
-<ng-template [ngIf]="fetchingFlag" [ngIfElse]="resultsTmpl">
-  <ng-container *ngTemplateOutlet="loadingSpinner">
-  </ng-container>
-</ng-template>
-
-<!-- else, show fetched -->
-<ng-template #resultsTmpl>
-
-  <!-- if error, show error only -->
-  <ng-template [ngIf]="fetchError">
-    <ng-container *ngTemplateOutlet="errorTemplate">
-    </ng-container>
-  </ng-template>
-
-  <!-- if not error, show dataset template -->
-  
-  <ng-template [ngIf]="!fetchError">
-    <ng-template [ngTemplateOutlet]="datasetListTmpl"
-      [ngIf]="disableVirtualScroll"
-      [ngIfElse]="datasetVirtualScrollTmpl">
-
-    </ng-template>
-  </ng-template>
-</ng-template>
-
-<!-- footer, populated by content transclusion -->
-<ng-content select="[card-footer]">
-</ng-content>
-
-<ng-template #loadingSpinner>
-  <mat-card-content class="h-100 d-flex justify-content-start p-2">
-    <spinner-cmp class="mr-2"></spinner-cmp>
-    <span>Fetching datasets...</span>
-  </mat-card-content>
-</ng-template>
-
-<ng-template #errorTemplate>
-  <mat-card-content>
-    <div class="ml-2 mr-2 alert alert-danger">
-      <i class="fas fa-exclamation-triangle"></i> Error fetching data. <a href="#" (click)="retryFetchData($event)" class="btn btn-link text-info">retry</a>    
-    </div>
-  </mat-card-content>
-</ng-template>
-
-<ng-template #datasetVirtualScrollTmpl>
-  <!-- datawrapper -->
-
-  <ng-container *ngIf="dataentries | filterDataEntriesByMethods : visibleCountedDataM as filteredDataEntry">
-    <mat-card-content class="dataset-container w-100 overflow-hidden">
-      <!-- TODO export aria labels to common/constants -->
-      <cdk-virtual-scroll-viewport
-        *ngIf="showList"
-        [attr.aria-label]="LIST_OF_DATASETS_ARIA_LABEL"
-        class="h-100"
-        minBufferPx="200"
-        maxBufferPx="400"
-        itemSize="50">
-        <div *cdkVirtualFor="let dataset of filteredDataEntry; trackBy: trackByFn; templateCacheSize: 20; let index = index"
-          class="virtual-scroll-element overflow-hidden">
-
-          <!-- divider, show if not first -->
-          <mat-divider *ngIf="index !== 0"></mat-divider>
-
-          <single-dataset-list-view
-            class="d-block pt-1 pb-1 h-100"
-            [kgSchema]="(dataset.fullId | getKgSchemaIdFromFullIdPipe)[0]"
-            [kgId]="(dataset.fullId | getKgSchemaIdFromFullIdPipe)[1]"
-            [dataset]="dataset"
-            [ripple]="true">
-    
-          </single-dataset-list-view>
-          
-
-        </div>
-      </cdk-virtual-scroll-viewport>
-    </mat-card-content>
-  </ng-container>
-</ng-template>
-
-<ng-template #datasetListTmpl>
-
-  <ng-container *ngIf="dataentries | filterDataEntriesByMethods : visibleCountedDataM as filteredDataEntry">
-    <mat-card-content class="w-100">
-      <!-- TODO export aria labels to common/constants -->
-      <div *ngIf="showList">
-        <div *ngFor="let dataset of filteredDataEntry; trackBy: trackByFn; let index = index"
-          class="scroll-element overflow-hidden">
-
-          <mat-divider *ngIf="index !== 0"></mat-divider>
-
-          <single-dataset-list-view
-            class="d-block pt-1 pb-1 h-100"
-            [kgSchema]="(dataset.fullId | getKgSchemaIdFromFullIdPipe)[0]"
-            [kgId]="(dataset.fullId | getKgSchemaIdFromFullIdPipe)[1]"
-            [dataset]="dataset"
-            [ripple]="true">
-    
-          </single-dataset-list-view>
-          
-        </div>
-      </div>
-    </mat-card-content>
-  </ng-container>
-</ng-template>
-
-<!-- modality picker / filter -->
-<ng-template #modalitySelector>
-  <mat-accordion class="flex-grow-0 flex-shrink-0">
-
-    <!-- Filters -->
-    <mat-expansion-panel hideToggle>
-
-      <mat-expansion-panel-header class="align-items-center"
-        [attr.aria-label]="MODALITY_FILTER_ARIA_LABEL">
-        <mat-panel-title class="d-inline-flex align-items-center">
-          <div class="flex-grow-1 flex-shrink-1 d-flex flex-column">
-            <span>
-                Filter features
-            </span>
-            <small *ngIf="dataentries.length > 0" class="text-muted">
-              <ng-template [ngIf]="modalityPickerCmp && modalityPickerCmp.checkedModality.length > 0"
-                [ngIfElse]="noFilterTmpl">
-                {{ (dataentries | filterDataEntriesByMethods : visibleCountedDataM).length }} / {{ dataentries.length }}
-              </ng-template>
-              
-              <ng-template #noFilterTmpl>
-                {{ dataentries.length }} features
-              </ng-template>
-            </small>
-          </div>
-
-          <button mat-icon-button
-            [matTooltip]="visibleCountedDataM.length > 0 ? 'Reset filters' : null"
-            iav-delay-event="click"
-            (iav-delay-event-emit)="visibleCountedDataM.length > 0 ? clearAll() : null"
-            [iav-stop]="visibleCountedDataM.length > 0 ? 'click' : null"
-            [color]="visibleCountedDataM.length > 0 ? 'primary' : 'basic'">
-            <i class="fas fa-filter"></i>
-          </button>
-        </mat-panel-title>
-
-      </mat-expansion-panel-header>
-
-      <div class="max-h-10em overflow-y-auto overflow-x-hidden">
-        <modality-picker
-          iav-stop="click"
-          class="w-100"
-          [countedDataM]="visibleCountedDataM | resetcounterModalityPipe | appendFilterModalityPipe : [countedDataM]"
-          (modalityFilterEmitter)="handleModalityFilterEvent($event)"
-          #modalityPickerCmp>
-      
-        </modality-picker>
-      </div>
-
-    </mat-expansion-panel>
-  </mat-accordion>
-
-</ng-template>
diff --git a/src/ui/databrowserModule/preview/filterPreview.pipe.ts b/src/ui/databrowserModule/preview/filterPreview.pipe.ts
deleted file mode 100644
index 21e909d58b905e5b27ddc6d46f0196164421ac64..0000000000000000000000000000000000000000
--- a/src/ui/databrowserModule/preview/filterPreview.pipe.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-import { EnumPreviewFileTypes } from "../pure";
-import { ViewerPreviewFile } from "src/services/state/dataStore.store";
-import { determinePreviewFileType } from "../constants";
-
-@Pipe({
-  name: 'filterPreviewByType'
-})
-
-export class FilterPreviewByType implements PipeTransform{
-  public transform(files: ViewerPreviewFile[], types: EnumPreviewFileTypes[]){
-    return files.filter(f => {
-      const currentFileType = determinePreviewFileType(f)
-      return types.includes(currentFileType) 
-    })
-  }
-}
\ No newline at end of file
diff --git a/src/ui/databrowserModule/preview/previewCard/previewCard.component.ts b/src/ui/databrowserModule/preview/previewCard/previewCard.component.ts
deleted file mode 100644
index 73ddae99dc17d0574fccbd2ac4890d7a44ec4b96..0000000000000000000000000000000000000000
--- a/src/ui/databrowserModule/preview/previewCard/previewCard.component.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { Component, Optional, Inject } from "@angular/core";
-import { PreviewBase } from "../preview.base";
-import { GET_KGDS_PREVIEW_INFO_FROM_ID_FILENAME } from "../../pure";
-
-// TODO deprecate in favour of datafeature/region feature
-
-@Component({
-  selector: 'preview-card',
-  templateUrl: './previewCard.template.html',
-  styleUrls: [
-    './previewCard.style.css'
-  ]
-})
-
-export class PreviewCardComponent extends PreviewBase {
-  constructor(
-    @Optional() @Inject(GET_KGDS_PREVIEW_INFO_FROM_ID_FILENAME) getDatasetPreviewFromId,
-  ){
-    super(getDatasetPreviewFromId)
-  }
-}
diff --git a/src/ui/databrowserModule/preview/previewCard/previewCard.style.css b/src/ui/databrowserModule/preview/previewCard/previewCard.style.css
deleted file mode 100644
index 8d0266fe77710f71d9e50865274cd336aaf8cecc..0000000000000000000000000000000000000000
--- a/src/ui/databrowserModule/preview/previewCard/previewCard.style.css
+++ /dev/null
@@ -1,6 +0,0 @@
-.header-container
-{
-  padding: 16px;
-  margin: -16px!important;
-  padding-top: 6rem;
-}
diff --git a/src/ui/databrowserModule/preview/previewCard/previewCard.template.html b/src/ui/databrowserModule/preview/previewCard/previewCard.template.html
deleted file mode 100644
index 697ef4010d189ef662977b2c2716363b6d4e53fe..0000000000000000000000000000000000000000
--- a/src/ui/databrowserModule/preview/previewCard/previewCard.template.html
+++ /dev/null
@@ -1,115 +0,0 @@
-<mat-card class="mat-elevation-z4">
-  <div class="header-container bg-50-grey-20">
-    <mat-card-title>
-      {{ singleDsView?.name || file.name || filename }}
-    </mat-card-title>
-
-    <mat-card-subtitle class="d-inline-flex align-items-center">
-      <mat-icon fontSet="fas" fontIcon="fa-database"></mat-icon>
-      <span>
-        Dataset preview
-      </span>
-      
-      <mat-divider [vertical]="true" class="ml-2 h-2rem"></mat-divider>
-
-      <!-- explore btn -->
-      <a *ngFor="let kgRef of singleDsView.kgReference"
-        [href]="kgRef | doiParserPipe"
-        class="color-inherit"
-        target="_blank">
-        <button mat-icon-button
-          [matTooltip]="singleDsView.EXPLORE_DATASET_IN_KG_ARIA_LABEL">
-          <i class="fas fa-external-link-alt"></i>
-        </button>
-      </a>
-
-      <!-- pin dataset btn -->
-      <ng-container *ngTemplateOutlet="favDatasetBtn; context: { singleDataset: singleDsView }">
-      </ng-container>
-
-      <!-- download zip btn -->
-      <a *ngIf="singleDsView.files && singleDsView.files.length > 0"
-        [href]="singleDsView.dlFromKgHref"
-        class="color-inherit"
-        target="_blank">
-        <button mat-icon-button
-          [matTooltip]="singleDsView.tooltipText"
-          [disabled]="singleDsView.downloadInProgress">
-          <i class="ml-1 fas" [ngClass]="!singleDsView.downloadInProgress? 'fa-download' :'fa-spinner fa-pulse'"></i>
-        </button>
-      </a>
-      
-    </mat-card-subtitle>
-  </div>
-
-  <mat-card-content class="mt-2 ml-15px-n mr-15px-n pb-4">
-    <mat-accordion>
-      <mat-expansion-panel hideToggle
-        [expanded]="true">
-    
-        <mat-expansion-panel-header>
-          <mat-panel-title>
-            Description
-          </mat-panel-title>
-        </mat-expansion-panel-header>
-    
-        <ng-template matExpansionPanelContent>
-          <single-dataset-view [fullId]="datasetId"
-            [hideTitle]="true"
-            [hidePreview]="true"
-            [hideExplore]="true"
-            [hidePinBtn]="true"
-            [hideDownloadBtn]="true"
-            #singleDsView="singleDatasetView">
-          
-          </single-dataset-view>
-        </ng-template>
-      </mat-expansion-panel>
-
-
-      <mat-expansion-panel hideToggle>
-
-        <mat-expansion-panel-header>
-          <mat-panel-title>
-            Registered Volumes
-          </mat-panel-title>
-        </mat-expansion-panel-header>
-
-        <ng-template matExpansionPanelContent>
-          <!-- TODO -->
-          <!-- this is not exactly right -->
-          <layer-browser class="ml-24px-n mr-24px-n"></layer-browser>
-        </ng-template>
-
-      </mat-expansion-panel>
-    </mat-accordion>
-    
-  </mat-card-content>
-</mat-card>
-
-<!-- templates -->
-<ng-template #favDatasetBtn let-singleDataset="singleDataset">
-  <ng-container *ngTemplateOutlet="isFavCtxTmpl; context: { isFav: (singleDataset.favedDataentries$ | async | datasetIsFaved : singleDataset.dataset) }">
-  </ng-container>
-
-  <ng-template #isFavCtxTmpl let-isFav="isFav">
-    <button mat-icon-button
-      (click)="isFav ? singleDataset.undoableRemoveFav() : singleDataset.undoableAddFav()"
-      [attr.aria-label]="singleDataset.PIN_DATASET_ARIA_LABEL"
-      [matTooltip]="singleDataset.PIN_DATASET_ARIA_LABEL"
-      [color]="isFav ? 'primary' : 'basic'">
-      <i class="fas fa-thumbtack"></i>
-    </button>
-  </ng-template>
-</ng-template>
-
-<single-dataset-view [fullId]="datasetId"
-  [hidden]="true"
-  [hideTitle]="true"
-  [hidePreview]="true"
-  [hideExplore]="true"
-  [hidePinBtn]="true"
-  [hideDownloadBtn]="true"
-  #singleDsView="singleDatasetView">
-
-</single-dataset-view>
\ No newline at end of file
diff --git a/src/ui/databrowserModule/preview/previewDatasetFile.directive.spec.ts b/src/ui/databrowserModule/preview/previewDatasetFile.directive.spec.ts
deleted file mode 100644
index 6d09d69bdc2b7c2b10e8631a7d2450fefb147e6a..0000000000000000000000000000000000000000
--- a/src/ui/databrowserModule/preview/previewDatasetFile.directive.spec.ts
+++ /dev/null
@@ -1,109 +0,0 @@
-import { Component } from "@angular/core";
-import { async, TestBed } from "@angular/core/testing";
-import { By } from "@angular/platform-browser";
-import { MatSnackBar } from "@angular/material/snack-bar";
-import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module";
-import { PreviewDatasetFile, IAV_DATASET_PREVIEW_DATASET_FN, IAV_DATASET_PREVIEW_ACTIVE } from './previewDatasetFile.directive'
-import { Subject } from "rxjs";
-
-@Component({
-  template: ''
-})
-
-class TestCmp{
-  testmethod(arg) {}
-}
-
-const dummyMatSnackBar = {
-  open: jasmine.createSpy('open')
-}
-
-const previewDatasetFnSpy = jasmine.createSpy('previewDatasetFn')
-const mockDatasetActiveObs = new Subject()
-const getDatasetActiveObs = jasmine.createSpy('getDatasetActive').and.returnValue(mockDatasetActiveObs)
-
-describe('ShowDatasetDialogDirective', () => {
-  let testModule
-  beforeEach(async(() => {
-    testModule = TestBed
-      .configureTestingModule({
-        imports: [
-          AngularMaterialModule
-        ],
-        declarations: [
-          TestCmp,
-          PreviewDatasetFile,
-        ],
-        providers: [
-          {
-            provide: MatSnackBar,
-            useValue: dummyMatSnackBar
-          },
-          {
-            provide: IAV_DATASET_PREVIEW_DATASET_FN,
-            useValue: previewDatasetFnSpy
-          },
-        ]
-      })
-      
-  }))
-
-  afterEach(() => {
-    dummyMatSnackBar.open.calls.reset()
-    previewDatasetFnSpy.calls.reset()
-  })
-
-  it('should be able to test directive', () => {
-
-    TestBed.overrideComponent(TestCmp, {
-      set: {
-        template: '<div iav-dataset-preview-dataset-file></div>',
-      }
-    }).compileComponents()
-
-    const fixutre = TestBed.createComponent(TestCmp)
-    const directive = fixutre.debugElement.query( By.directive( PreviewDatasetFile ) )
-
-    expect(directive).not.toBeNull()
-  })
-
-  it('without providing file or filename, should not call emitFn', () => {
-
-    TestBed.overrideComponent(TestCmp, {
-      set: {
-        template: '<div iav-dataset-preview-dataset-file></div>',
-      }
-    }).compileComponents()
-    
-    const fixutre = TestBed.createComponent(TestCmp)
-    fixutre.detectChanges()
-    const directive = fixutre.debugElement.query( By.directive( PreviewDatasetFile ) )
-    directive.nativeElement.click()
-
-    expect(dummyMatSnackBar.open).toHaveBeenCalled()
-    expect(previewDatasetFnSpy).not.toHaveBeenCalled()
-
-  })
-
-  it('only providing filename, should call emitFn', () => {
-
-    TestBed.overrideComponent(TestCmp, {
-      set: {
-        template: `
-        <div iav-dataset-preview-dataset-file
-          iav-dataset-preview-dataset-file-filename="banana">
-        </div>
-        `,
-      }
-    }).compileComponents()
-    
-    const fixutre = TestBed.createComponent(TestCmp)
-    fixutre.detectChanges()
-    const directive = fixutre.debugElement.query( By.directive( PreviewDatasetFile ) )
-    directive.nativeElement.click()
-
-    expect(dummyMatSnackBar.open).not.toHaveBeenCalled()
-    expect(previewDatasetFnSpy).toHaveBeenCalledWith({ filename: 'banana' }, { fullId: null })
-
-  })
-})
\ No newline at end of file
diff --git a/src/ui/databrowserModule/shownDataset.directive.ts b/src/ui/databrowserModule/shownDataset.directive.ts
deleted file mode 100644
index dd46b310241b7f35cefb95637b3174bfb27457a1..0000000000000000000000000000000000000000
--- a/src/ui/databrowserModule/shownDataset.directive.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { Directive } from "@angular/core";
-import { select, Store } from "@ngrx/store";
-import { Observable } from "rxjs";
-import { uiStateShownDatasetIdSelector } from "src/services/state/uiState/selectors";
-
-@Directive({
-  selector: '[iav-shown-dataset]',
-  exportAs: 'iavShownDataset'
-})
-
-export class ShownDatasetDirective{
-  public shownDatasetId$: Observable<string[]>
-  constructor(
-    store$: Store<any>
-  ){
-    this.shownDatasetId$ = store$.pipe(
-      select(uiStateShownDatasetIdSelector)
-    )
-  }
-}
diff --git a/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.component.spec.ts b/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.component.spec.ts
deleted file mode 100644
index 7cf7c9f2f7d0a11f35d7551131079eefaf7b299f..0000000000000000000000000000000000000000
--- a/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.component.spec.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-describe('singleDataset.component.ts', () => {
-  describe('', () => {
-
-  })
-})
\ No newline at end of file
diff --git a/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.component.ts b/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.component.ts
deleted file mode 100644
index d273ee2f1388898c08789dccb31a9e3ef4d5ee48..0000000000000000000000000000000000000000
--- a/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.component.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import { ChangeDetectorRef, Component, Inject, Optional, Input} from "@angular/core";
-import {
-  DatabrowserService,
-  KgSingleDatasetService,
-  SingleDatasetBase,
-} from "../singleDataset.base";
-import {MAT_DIALOG_DATA} from "@angular/material/dialog";
-import { MatSnackBar } from "@angular/material/snack-bar";
-
-@Component({
-  selector: 'single-dataset-view',
-  templateUrl: './singleDataset.template.html',
-  styleUrls: [
-    `./singleDataset.style.css`,
-  ],
-  exportAs: 'singleDatasetView'
-})
-
-export class SingleDatasetView extends SingleDatasetBase {
-
-  constructor(
-    dbService: DatabrowserService,
-    singleDatasetService: KgSingleDatasetService,
-    cdr: ChangeDetectorRef,
-    snackbar: MatSnackBar,
-    @Optional() @Inject(MAT_DIALOG_DATA) data: any,
-  ) {
-    super(dbService, singleDatasetService, cdr,snackbar, data)
-  }
-  
-  @Input()
-  hideTitle = false
-
-  @Input()
-  hidePreview = false
-
-  @Input()
-  hideExplore = false
-
-  @Input()
-  hidePinBtn = false
-
-  @Input()
-  hideDownloadBtn = false
-
-  @Input()
-  useSmallIcon = false
-}
diff --git a/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.style.css b/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.style.css
deleted file mode 100644
index 9c4f33e35402143057541d30e4c76fe566cba76d..0000000000000000000000000000000000000000
--- a/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.style.css
+++ /dev/null
@@ -1,4 +0,0 @@
-:host
-{
-  text-align: left
-}
\ No newline at end of file
diff --git a/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.template.html b/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.template.html
deleted file mode 100644
index 54e8c9e7be8ed518ad053151e79b6e1f12cfb574..0000000000000000000000000000000000000000
--- a/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.template.html
+++ /dev/null
@@ -1,162 +0,0 @@
-
-<!-- title -->
-<mat-card-subtitle *ngIf="!hideTitle">
-  <span *ngIf="name; else isLoadingTmpl">
-    {{ name }}
-  </span>
-</mat-card-subtitle>
-
-<mat-card-content mat-dialog-content>
-
-  <!-- description -->
-  <small>
-    <markdown-dom
-      *ngIf="description; else isLoadingTmpl"
-      class="d-block"
-      [markdown]="description">
-
-    </markdown-dom>
-  </small>
-
-  <!-- publications -->
-  <ng-container *ngIf="!strictLocal">
-
-    <small class="d-block mb-2"
-      *ngFor="let publication of publications">
-      <a *ngIf="publication.doi; else plainText"
-        iav-stop="click mousedown"
-        [href]="publication.doi | doiParserPipe"
-        target="_blank">
-        {{ publication.cite }}
-      </a>
-      <ng-template #plainText>
-        {{ publication.cite }}
-      </ng-template>
-    </small>
-  </ng-container>
-
-  <!-- contributors, if publications not available -->
-  <ng-container *ngIf="publications && publications.length == 0 && contributors && contributors.length > 0">
-    <ng-container *ngFor="let contributor of contributors; let lastFlag = last;">
-      <a [href]="contributor | getContributorKgLink" class="iv-custom-comp" target="_blank">
-        {{ contributor['schema.org/shortName'] || contributor['shortName'] || contributor['name'] }}
-      </a>
-      <span *ngIf="!lastFlag">,</span>
-    </ng-container>
-  </ng-container>
-</mat-card-content>
-
-
-<!-- footer -->
-<mat-card-actions iav-media-query #iavMediaQuery="iavMediaQuery">
-  <ng-container *ngTemplateOutlet="actionBtns; context: {
-    $implicit: useSmallIcon || (iavMediaQuery.mediaBreakPoint$ | async) > 1
-  }" >
-  </ng-container>
-</mat-card-actions>
-
-<mat-card-footer></mat-card-footer>
-
-<ng-template #previewFilesListTemplate>
-  <dataset-preview-list
-    [kgId]="kgId">
-
-  </dataset-preview-list>
-</ng-template>
-
-<!-- using ng template for context binding of media breakpoints -->
-<ng-template #actionBtns let-useSmallIcon>
-
-  <!-- explore -->
-  <ng-container *ngIf="!strictLocal && !hideExplore">
-
-    <a *ngFor="let kgRef of kgReference"
-      [href]="kgRef | doiParserPipe"
-      target="_blank">
-      <iav-dynamic-mat-button
-        [iav-dynamic-mat-button-style]="useSmallIcon ? 'mat-icon-button' : 'mat-raised-button'"
-        iav-dynamic-mat-button-color="primary">
-
-        <span *ngIf="!useSmallIcon">
-          Explore
-        </span>
-        <i class="fas fa-external-link-alt"></i>
-      </iav-dynamic-mat-button>
-    </a>
-  </ng-container>
-
-  <!-- pin data -->
-  <ng-container *ngIf="downloadEnabled && kgId">
-
-    <ng-container *ngTemplateOutlet="favDatasetBtn; context: { $implicit: (favedDataentries$ | async | datasetIsFaved : ({ fullId: fullId })) }">
-    </ng-container>
-
-    <ng-template #favDatasetBtn let-isFav>
-      <iav-dynamic-mat-button
-        *ngIf="!hidePinBtn"
-        (click)="isFav ? undoableRemoveFav() : undoableAddFav()"
-        iav-stop="click mousedown"
-        [iav-dynamic-mat-button-aria-label]="PIN_DATASET_ARIA_LABEL"
-        [iav-dynamic-mat-button-style]="useSmallIcon ? 'mat-icon-button' : 'mat-button'"
-        [iav-dynamic-mat-button-color]="isFav ? 'primary' : 'basic'">
-
-        <span *ngIf="!useSmallIcon">
-          {{ isFav ? 'Unpin this dataset' : 'Pin this dataset' }}
-        </span>
-        <i class="fas fa-thumbtack"></i>
-      </iav-dynamic-mat-button>
-    </ng-template>
-  </ng-container>
-
-  <!-- download -->
-  <ng-container *ngIf="!strictLocal && !hideDownloadBtn">
-
-    <a *ngIf="files && files.length > 0"
-      [href]="dlFromKgHref"
-      target="_blank">
-      <iav-dynamic-mat-button
-        [matTooltip]="tooltipText"
-        [disabled]="downloadInProgress"
-        [iav-dynamic-mat-button-style]="useSmallIcon ? 'mat-icon-button' : 'mat-button'">
-
-        <span *ngIf="!useSmallIcon">
-          Download Zip
-        </span>
-        <i class="ml-1 fas" [ngClass]="!downloadInProgress? 'fa-download' :'fa-spinner fa-pulse'"></i>
-      </iav-dynamic-mat-button>
-    </a>
-  </ng-container>
-
-
-  <!-- check if has preview -->
-
-  <ng-template [ngIf]="!hidePreview">
-
-    <kg-dataset-list
-      class="d-none"
-      [backendUrl]="DS_PREVIEW_URL"
-      *ngIf="kgId"
-      (kgDsPrvUpdated)="handleKgDsPrvUpdate($event)"
-      [kgId]="kgId">
-
-    </kg-dataset-list>
-
-    <iav-dynamic-mat-button
-      *ngIf="hasPreview"
-      mat-dialog-close
-      [iav-dynamic-mat-button-style]="useSmallIcon ? 'mat-icon-button' : 'mat-button'"
-      [iav-dynamic-mat-button-aria-label]="SHOW_DATASET_PREVIEW_ARIA_LABEL"
-      (click)="showPreviewList(previewFilesListTemplate)">
-
-      <span *ngIf="!useSmallIcon">
-        Preview
-      </span>
-      <i class="ml-1 far fa-eye"></i>
-    </iav-dynamic-mat-button>
-  </ng-template>
-
-</ng-template>
-
-<ng-template #isLoadingTmpl>
-  <spinner-cmp></spinner-cmp>
-</ng-template>
\ No newline at end of file
diff --git a/src/ui/databrowserModule/singleDataset/sideNavView/sDsSideNavView.component.ts b/src/ui/databrowserModule/singleDataset/sideNavView/sDsSideNavView.component.ts
deleted file mode 100644
index fe8eb871cc1b166fcf962add30a7402f3d907313..0000000000000000000000000000000000000000
--- a/src/ui/databrowserModule/singleDataset/sideNavView/sDsSideNavView.component.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, OnChanges, OnDestroy, Optional, Output, EventEmitter } from "@angular/core";
-import { MatSnackBar } from "@angular/material/snack-bar";
-import { Observable } from "rxjs";
-import { REGION_OF_INTEREST, TRegionOfInterest } from "src/util/interfaces";
-import { DatabrowserService } from "../../databrowser.service";
-import { KgSingleDatasetService } from "../../kgSingleDatasetService.service";
-import { SingleDatasetBase } from "../singleDataset.base";
-import { CONST, ARIA_LABELS } from 'common/constants'
-
-@Component({
-  selector: 'single-dataset-sidenav-view',
-  templateUrl: './sDsSideNavView.template.html',
-  styleUrls: [
-    './sDsSideNavView.style.css'
-  ],
-  changeDetection: ChangeDetectionStrategy.OnPush,
-})
-
-export class SingleDatasetSideNavView extends SingleDatasetBase implements OnChanges, OnDestroy{
-  public BACK_BTN_ARIA_TXT = ARIA_LABELS.CLOSE
-  @Output()
-  clear: EventEmitter<null> = new EventEmitter()
-
-  public GDPR_TEXT = CONST.GDPR_TEXT
-
-  constructor(
-    dbService: DatabrowserService,
-    sDsService: KgSingleDatasetService,
-    private _cdr: ChangeDetectorRef,
-    snackBar: MatSnackBar,
-    @Optional() @Inject(REGION_OF_INTEREST) public region$: Observable<TRegionOfInterest>
-  ){
-    super(
-      dbService,
-      sDsService,
-      _cdr,
-      snackBar,
-    )
-  }
-  ngOnDestroy(){
-    super.ngOnDestroy()
-  }
-  ngOnChanges(){
-    super.ngOnChanges()
-  }
-  
-  detectChange(){
-    this._cdr.detectChanges()
-  }
-}
diff --git a/src/ui/databrowserModule/singleDataset/sideNavView/sDsSideNavView.style.css b/src/ui/databrowserModule/singleDataset/sideNavView/sDsSideNavView.style.css
deleted file mode 100644
index ccdddfd5e46b96c4f33db7ef7e6370dd1bd59f02..0000000000000000000000000000000000000000
--- a/src/ui/databrowserModule/singleDataset/sideNavView/sDsSideNavView.style.css
+++ /dev/null
@@ -1,11 +0,0 @@
-:host
-{
-  position: relative;
-}
-
-.header-container
-{
-  padding: 16px;
-  margin: -16px!important;
-  padding-top: 6rem;
-}
diff --git a/src/ui/databrowserModule/singleDataset/sideNavView/sDsSideNavView.template.html b/src/ui/databrowserModule/singleDataset/sideNavView/sDsSideNavView.template.html
deleted file mode 100644
index 455219a1825c31e207e94e6ea46a36774c6871ad..0000000000000000000000000000000000000000
--- a/src/ui/databrowserModule/singleDataset/sideNavView/sDsSideNavView.template.html
+++ /dev/null
@@ -1,169 +0,0 @@
-<button mat-button
-  [attr.aria-label]="BACK_BTN_ARIA_TXT"
-  class="position-absolute z-index-10 m-2"
-  (click)="clear.emit()">
-  <i class="fas fa-chevron-left"></i>
-  <span class="ml-1">
-    Back
-  </span>
-</button>
-
-<mat-card class="mat-elevation-z4">
-  <div class="header-container bg-50-grey-20">
-    <mat-card-title>
-      <ng-content select="[region-of-interest]"></ng-content>
-      <div *ngIf="!fetchFlag; else isLoadingTmpl">
-        {{ name }}
-      </div>
-    </mat-card-title>
-
-    <mat-card-subtitle class="d-inline-flex align-items-center">
-      <mat-icon fontSet="fas" fontIcon="fa-database"></mat-icon>
-      <span>
-        Dataset
-      </span>
-
-      <button *ngIf="isGdprProtected"
-        [matTooltip]="GDPR_TEXT"
-        mat-icon-button color="warn">
-        <i class="fas fa-exclamation-triangle"></i>
-      </button>
-
-      <mat-divider [vertical]="true" class="ml-2 h-2rem"></mat-divider>
-
-      <!-- explore btn -->
-      <a *ngFor="let kgRef of kgReference"
-        [href]="kgRef | doiParserPipe"
-        class="color-inherit"
-        mat-icon-button
-        [matTooltip]="EXPLORE_DATASET_IN_KG_ARIA_LABEL"
-        target="_blank">
-        <i class="fas fa-external-link-alt"></i>
-      </a>
-
-      <!-- in case no doi is available, directly link to KG -->
-      <ng-template [ngIf]="kgReference.length === 0">
-        <a [href]="directLinkToKg"
-          class="color-inherit"
-          mat-icon-button
-          [matTooltip]="EXPLORE_DATASET_IN_KG_ARIA_LABEL"
-          target="_blank">
-          <i class="fas fa-external-link-alt"></i>
-        </a>
-      </ng-template>
-
-      <!-- fav btn -->
-      <ng-container *ngTemplateOutlet="favDatasetBtn">
-      </ng-container>
-      
-    </mat-card-subtitle>
-  </div>
-
-  <mat-card-content class="mt-2 ml-15px-n mr-15px-n pb-4">
-    <mat-accordion>
-
-      <!-- Description -->
-      <mat-expansion-panel>
-        <mat-expansion-panel-header>
-          <mat-panel-title>
-            Description
-          </mat-panel-title>
-        </mat-expansion-panel-header>
-        <ng-template matExpansionPanelContent>
-          <small *ngIf="!fetchFlag; else isLoadingTmpl" class="m-1">
-
-            <!-- desc -->
-            <markdown-dom [markdown]="description">
-            </markdown-dom>
-
-            <mat-divider></mat-divider>
-
-            <!-- citations -->
-            <div class="d-block mb-2 "
-              [ngClass]="{'mt-2': first}"
-              *ngFor="let publication of publications; let first = first">
-              <a *ngIf="publication.doi; else plainText"
-                iav-stop="click mousedown"
-                [href]="publication.doi | doiParserPipe"
-                target="_blank">
-                {{ publication.cite }}
-              </a>
-              <ng-template #plainText>
-                {{ publication.cite }}
-              </ng-template>
-            </div>
-
-            <!-- contributors, if publications not available -->
-            <ng-container *ngIf="publications && publications.length == 0 && contributors && contributors.length > 0">
-              <ng-container *ngFor="let contributor of contributors; let lastFlag = last;">
-                <a [href]="contributor | getContributorKgLink" class="iv-custom-comp" target="_blank">
-                  {{ contributor['schema.org/shortName'] || contributor['shortName'] || contributor['name'] }}
-                </a>
-                <span *ngIf="!lastFlag">,</span>
-              </ng-container>
-            </ng-container>
-
-          </small>
-        </ng-template>
-      </mat-expansion-panel>
-
-      <!-- Features -->
-      <div class="hidden"
-        [region]="region$ | async"
-        (loadingStateChanged)="detectChange()"
-        region-get-all-features-directive
-        #rfGetAllFeatures="rfGetAllFeatures">
-      </div>
-
-      <!-- loading tmpl -->
-      <ng-template [ngIf]="rfGetAllFeatures.isLoading$ | async" [ngIfElse]="featureTmpl">
-        <div class="d-flex justify-content-center">
-          <ng-container *ngTemplateOutlet="isLoadingTmpl"></ng-container>
-        </div>
-      </ng-template>
-
-      <!-- feature tmpl -->
-      <ng-template #featureTmpl>
-        <ng-container *ngFor="let feature of (rfGetAllFeatures.features | filterRegionFeaturesById : fullId)">
-          <mat-expansion-panel #matExpansionPanel>
-            <mat-expansion-panel-header>
-              <mat-panel-title>
-                {{ feature.type }}
-              </mat-panel-title>
-            </mat-expansion-panel-header>
-  
-            <ng-template [ngIf]="matExpansionPanel.expanded">
-              <feature-container
-                [feature]="feature"
-                [region]="region$ | async"
-                (viewChanged)="detectChange()">
-              </feature-container>
-            </ng-template>
-          </mat-expansion-panel>
-        </ng-container>
-      </ng-template>
-    </mat-accordion>
-
-  </mat-card-content>
-</mat-card>
-
-<ng-template #favDatasetBtn>
-  <ng-container *ngTemplateOutlet="isFavCtxTmpl; context: {
-    isFav: (favedDataentries$ | async | datasetIsFaved : ({ fullId: fullId }))
-  }">
-  </ng-container>
-
-  <ng-template #isFavCtxTmpl let-isFav="isFav">
-    <button mat-icon-button
-      (click)="isFav ? undoableRemoveFav() : undoableAddFav()"
-      [attr.aria-label]="PIN_DATASET_ARIA_LABEL"
-      [matTooltip]="PIN_DATASET_ARIA_LABEL"
-      [color]="isFav ? 'primary' : 'basic'">
-      <i class="fas fa-thumbtack"></i>
-    </button>
-  </ng-template>
-</ng-template>
-
-<ng-template #isLoadingTmpl>
-  <spinner-cmp></spinner-cmp>
-</ng-template>
\ No newline at end of file
diff --git a/src/ui/databrowserModule/util/appendFilterModality.pipe.ts b/src/ui/databrowserModule/util/appendFilterModality.pipe.ts
deleted file mode 100644
index 7894ca1bed066cb3eb9fef91a6c43212d6cee282..0000000000000000000000000000000000000000
--- a/src/ui/databrowserModule/util/appendFilterModality.pipe.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-import { CountedDataModality } from "../databrowser.service";
-
-@Pipe({
-  name: 'appendFilterModalityPipe',
-})
-
-export class AppendFilerModalityPipe implements PipeTransform {
-  public transform(root: CountedDataModality[], appending: CountedDataModality[][]): CountedDataModality[] {
-    let returnArr: CountedDataModality[] = [...root]
-    for (const mods of appending) {
-      for (const mod of mods) {
-        // preserve the visibility
-        const { visible } = returnArr.find(({ name }) => name === mod.name) || mod
-        returnArr = returnArr.filter(({ name }) => name !== mod.name)
-        returnArr = returnArr.concat({
-          ...mod,
-          visible,
-        })
-      }
-    }
-    return returnArr
-  }
-}
diff --git a/src/ui/databrowserModule/util/resetCounterModality.pipe.ts b/src/ui/databrowserModule/util/resetCounterModality.pipe.ts
deleted file mode 100644
index 484c3aaaf5357ff9ec3eba57cee7b216d792d3e5..0000000000000000000000000000000000000000
--- a/src/ui/databrowserModule/util/resetCounterModality.pipe.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-import { CountedDataModality } from "../databrowser.service";
-
-@Pipe({
-  name: 'resetcounterModalityPipe',
-})
-
-export class ResetCounterModalityPipe implements PipeTransform {
-  public transform(inc: CountedDataModality[]): CountedDataModality[] {
-    return inc.map(({ occurance:_occurance, ...rest }) => {
-      return {
-        occurance: 0,
-        ...rest,
-      }
-    })
-  }
-}
diff --git a/src/ui/help/about/about.component.ts b/src/ui/help/about/about.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..77dd5c431ba9ec42fc8911ead646806abeda0832
--- /dev/null
+++ b/src/ui/help/about/about.component.ts
@@ -0,0 +1,23 @@
+import { Component } from '@angular/core'
+import { PureContantService } from 'src/util';
+
+@Component({
+  selector: 'iav-about',
+  templateUrl: './about.template.html',
+  styleUrls: [
+    './about.style.css',
+  ],
+})
+
+export class AboutCmp {
+  public contactEmailHref: string = `mailto:${this.constantService.supportEmailAddress}?Subject=[InteractiveAtlasViewer]%20Queries`
+  public supportEmailAddress: string = this.constantService.supportEmailAddress
+
+  public userDoc: string = this.constantService.docUrl
+  public repoUrl = this.constantService.repoUrl
+
+  constructor(
+    private constantService: PureContantService,
+  ) {
+  }
+}
diff --git a/src/ui/help/help.style.css b/src/ui/help/about/about.style.css
similarity index 100%
rename from src/ui/help/help.style.css
rename to src/ui/help/about/about.style.css
diff --git a/src/ui/help/help.template.html b/src/ui/help/about/about.template.html
similarity index 100%
rename from src/ui/help/help.template.html
rename to src/ui/help/about/about.template.html
diff --git a/src/ui/help/help.component.ts b/src/ui/help/help.component.ts
deleted file mode 100644
index af6c46dff917b60fcf74c86fcb89a5d62eb6de54..0000000000000000000000000000000000000000
--- a/src/ui/help/help.component.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import { Component } from '@angular/core'
-import { DomSanitizer } from '@angular/platform-browser';
-import { AtlasViewerConstantsServices } from 'src/atlasViewer/atlasViewer.constantService.service';
-
-@Component({
-  selector: 'help-component',
-  templateUrl: './help.template.html',
-  styleUrls: [
-    './help.style.css',
-  ],
-})
-
-export class HelpComponent {
-
-  public generalHelp = this.constantService.showHelpGeneralMap
-  public sliceviewHelp = this.constantService.showHelpSliceViewMap
-  public perspectiveviewHelp = this.constantService.showHelpPerspectiveViewMap
-  public supportText = this.sanitizer.bypassSecurityTrustHtml(this.constantService.showHelpSupportText)
-
-  public contactEmailHref: string = `mailto:${this.constantService.supportEmailAddress}?Subject=[InteractiveAtlasViewer]%20Queries`
-  public supportEmailAddress: string = this.constantService.supportEmailAddress
-
-  public userDoc: string = this.constantService.docUrl
-  public repoUrl = this.constantService.repoUrl
-
-  constructor(
-    private constantService: AtlasViewerConstantsServices,
-    private sanitizer: DomSanitizer,
-  ) {
-  }
-}
diff --git a/src/ui/helpOnePager/helpOnePager.component.spec.ts b/src/ui/help/helpOnePager/helpOnePager.component.spec.ts
similarity index 91%
rename from src/ui/helpOnePager/helpOnePager.component.spec.ts
rename to src/ui/help/helpOnePager/helpOnePager.component.spec.ts
index 5856db9df27ecf555c8cc71d1a25c3fedb62ec40..95d4893670e85bb6cbe7a5c8c1e957d742c43ead 100644
--- a/src/ui/helpOnePager/helpOnePager.component.spec.ts
+++ b/src/ui/help/helpOnePager/helpOnePager.component.spec.ts
@@ -1,6 +1,7 @@
 import { CommonModule } from '@angular/common'
 import { async, TestBed } from '@angular/core/testing'
 import { ComponentsModule } from 'src/components'
+import { AngularMaterialModule } from 'src/ui/sharedModules/angularMaterial.module'
 import { PureContantService, UtilModule } from 'src/util'
 import { HelpOnePager } from './helpOnePager.component'
 
@@ -15,6 +16,7 @@ describe('> helpOnePager.component.ts', () => {
            * for media query directive
            */
           UtilModule,
+          AngularMaterialModule,
         ],
         declarations: [
           HelpOnePager,
diff --git a/src/ui/helpOnePager/helpOnePager.component.ts b/src/ui/help/helpOnePager/helpOnePager.component.ts
similarity index 69%
rename from src/ui/helpOnePager/helpOnePager.component.ts
rename to src/ui/help/helpOnePager/helpOnePager.component.ts
index 982360f4dc1d0deeb9cd2fa70d14e29bd31d2bf5..0e0cf92a6c9d4d936b9730947f95332916ca3f53 100644
--- a/src/ui/helpOnePager/helpOnePager.component.ts
+++ b/src/ui/help/helpOnePager/helpOnePager.component.ts
@@ -1,6 +1,7 @@
 import { Component, Optional } from "@angular/core";
 import QUICK_STARTER from '!!raw-loader!common/helpOnePager.md'
 import { PureContantService } from "src/util";
+import { ARIA_LABELS } from 'common/constants'
 
 @Component({
   selector: 'help-one-pager',
@@ -11,13 +12,16 @@ import { PureContantService } from "src/util";
 })
 
 export class HelpOnePager{
+  public ARIA_LABELS = ARIA_LABELS
   public QUICK_STARTER_MD = QUICK_STARTER
   public extQuickStarter: string
+  public userDoc: string
   constructor(
     @Optional() pConstService: PureContantService
   ){
-    if (pConstService?.backendUrl) {
-      this.extQuickStarter = `${pConstService.backendUrl}quickstart`
+    this.extQuickStarter = `quickstart.html`
+    if (pConstService) {
+      this.userDoc = pConstService.docUrl
     }
   }
 }
diff --git a/src/ui/help/helpOnePager/helpOnePager.style.css b/src/ui/help/helpOnePager/helpOnePager.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/ui/help/helpOnePager/helpOnePager.template.html b/src/ui/help/helpOnePager/helpOnePager.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..ebf014b77a1c45468f6e56966756a0f542563c37
--- /dev/null
+++ b/src/ui/help/helpOnePager/helpOnePager.template.html
@@ -0,0 +1,39 @@
+<div class="position-relative">
+  <markdown-dom mat-dialog-content [markdown]="QUICK_STARTER_MD">
+  </markdown-dom>
+
+  <a *ngIf="extQuickStarter"
+    [href]="extQuickStarter"
+    target="_blank"
+    class="position-absolute top-0 right-0"
+    [matTooltip]="ARIA_LABELS.OPEN_IN_NEW_WINDOW">
+    <button mat-icon-button
+      color="primary">
+      <i class = "m-1 fas fa-external-link-alt"></i>
+    </button>
+  </a>
+</div>
+
+<div mat-dialog-actions align="center">
+  <button mat-button color="primary" mat-dialog-close quick-tour-opener>
+    <i class = "far fa-play-circle m-1"></i>
+    <span>
+      Take a tour
+    </span>
+  </button>
+
+  <a *ngIf="userDoc"
+    [href]="userDoc"
+    target="_blank">
+    <button mat-button
+      color="primary">
+      <i class="m1 fas fa-book-open"></i>
+      <span>
+        User documentation
+      </span>
+      <i class="m-1 fas fa-external-link-alt"></i>
+    </button>
+  </a>
+
+  <button mat-button mat-dialog-close cdkFocusInitial>close</button>
+</div>
diff --git a/src/ui/help/index.ts b/src/ui/help/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/ui/help/module.ts b/src/ui/help/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9decffaa2985077251f945e7a55c20b1924390d7
--- /dev/null
+++ b/src/ui/help/module.ts
@@ -0,0 +1,28 @@
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { ComponentsModule } from "src/components";
+import { UtilModule } from "src/util";
+import { AngularMaterialModule } from "../sharedModules/angularMaterial.module";
+import { AboutCmp } from './about/about.component'
+import { HelpOnePager } from "./helpOnePager/helpOnePager.component";
+import {QuickTourModule} from "src/ui/quickTour/module";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    AngularMaterialModule,
+    ComponentsModule,
+    UtilModule,
+    QuickTourModule
+  ],
+  declarations: [
+    AboutCmp,
+    HelpOnePager,
+  ],
+  exports: [
+    AboutCmp,
+    HelpOnePager,
+  ]
+})
+
+export class HelpModule{}
diff --git a/src/ui/helpOnePager/helpOnePager.template.html b/src/ui/helpOnePager/helpOnePager.template.html
deleted file mode 100644
index c309155ae5174ca4c62034fc8ab758913cbc71a3..0000000000000000000000000000000000000000
--- a/src/ui/helpOnePager/helpOnePager.template.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<markdown-dom mat-dialog-content [markdown]="QUICK_STARTER_MD">
-</markdown-dom>
-
-<div mat-dialog-actions align="center">
-
-  <a *ngIf="extQuickStarter"
-    [href]="extQuickStarter"
-    target="_blank">
-    <button mat-raised-button
-      color="primary">
-      <span iav-media-query #iavMediaQuery="iavMediaQuery">
-        Open <ng-template [ngIf]="(iavMediaQuery.mediaBreakPoint$ | async) < 3">in new window</ng-template>
-      </span>
-      <i class = "fas fa-external-link-alt"></i>
-    </button>
-  </a>
-
-  <button mat-button mat-dialog-close cdkFocusInitial>close</button>
-</div>
\ No newline at end of file
diff --git a/src/ui/kgEntryViewer/kgentry.component.ts b/src/ui/kgEntryViewer/kgentry.component.ts
deleted file mode 100644
index fada06ab098aafc41acc8f27848455897c1258e0..0000000000000000000000000000000000000000
--- a/src/ui/kgEntryViewer/kgentry.component.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { Component, Input } from "@angular/core";
-import { IDataEntry } from "src/services/stateStore.service";
-
-@Component({
-  selector : 'kg-entry-viewer',
-  templateUrl : './kgentry.template.html',
-  styleUrls : [
-    './kgentry.style.css',
-  ],
-})
-
-export class KgEntryViewer {
-  @Input() public dataset: IDataEntry
-
-  public kgData: any = null
-  public kgError: any = null
-
-  get tableColClass1() {
-    return `col-xs-4 col-lg-4 tableEntry`
-  }
-
-  get tableColClass2() {
-    return `col-xs-8 col-lg-8 tableEntry`
-  }
-
-  public isArray(v) {
-    return v.constructor === Array
-  }
-}
diff --git a/src/ui/kgEntryViewer/kgentry.style.css b/src/ui/kgEntryViewer/kgentry.style.css
deleted file mode 100644
index 4ac3fc6f4bd8f118c0339fff562389ee4d18239c..0000000000000000000000000000000000000000
--- a/src/ui/kgEntryViewer/kgentry.style.css
+++ /dev/null
@@ -1,34 +0,0 @@
-div[heading],
-div[body]
-{
-  padding: 0.5rem;
-}
-
-div[heading]
-{
-  background-color:rgba(128,128,128,0.1);
-}
-
-div[body]
-{
-  font-size: 90%;
-  max-height:20em;
-  overflow-y:auto;
-  overflow-x:hidden;
-}
-
-div[container]
-{
-  overflow:hidden;
-}
-
-a[link]
-{
-  display: inline-block;
-  margin-top: 1rem;
-}
-
-a[link]:hover
-{
-  text-decoration: none;
-}
\ No newline at end of file
diff --git a/src/ui/kgEntryViewer/kgentry.template.html b/src/ui/kgEntryViewer/kgentry.template.html
deleted file mode 100644
index cb309ebd9a5bc6fc307968557c3e7357ef8b0faf..0000000000000000000000000000000000000000
--- a/src/ui/kgEntryViewer/kgentry.template.html
+++ /dev/null
@@ -1,84 +0,0 @@
-<div *ngIf="dataset" container>
-
-  <!-- description -->
-  <readmore-component>
-    {{ dataset.description }}
-  </readmore-component>
-
-  <!-- other data -->
-
-  <panel-component *ngIf = "false"  [collapseBody] = "true" [bodyCollapsable] = "true">
-    <div heading>
-      Contributor(s)
-    </div>
-    <div body>
-      <!-- TODO maybe object instead of array -->
-      <div *ngFor = "let contributor of dataset.contributors">
-        {{ contributor.value }}
-      </div>
-    </div>
-  </panel-component>
-
-  <panel-component [collapseBody] = "true" [bodyCollapsable] = "true">
-    <div heading>
-      Custodian(s)
-    </div>
-    <div body>
-      <!-- TODO Maybe array -->
-      <div *ngFor="let custodian of dataset.custodians">
-        {{ custodian }}
-      </div>
-    </div>
-  </panel-component>
-
-  <panel-component *ngIf = "dataset.publications" [collapseBody] = "true" [bodyCollapsable] = "true">
-    <div heading>
-      Related Publication(s)
-    </div>
-    <div body>
-      <div *ngFor="let publication of dataset.publications">
-        <a target="_blank" link [href]="'https://doi.org/' + publication.doi">
-          {{ publication.cite }}
-          <i class = "fas fa-new-window"></i>
-        </a>
-      </div>
-    </div>
-  </panel-component>
-
-  <panel-component *ngIf = "dataset.licenseInfo || dataset.license" [collapseBody] = "true" [bodyCollapsable] = "true">
-    <div heading>
-      License
-    </div>
-    <div body>
-      <div *ngFor="let l of dataset.license">
-        {{ l }}
-      </div>
-      <div *ngFor="let l of dataset.licenseInfo">
-        {{ l }}
-      </div>
-    </div>
-  </panel-component>
-
-  <panel-component *ngIf = "dataset.files" [collapseBody] = "true" [bodyCollapsable] = "true">
-    <div heading>
-      Files
-    </div>
-    <div body>
-      <div *ngFor="let file of dataset.files">
-        <a link target="_blank" [href]="file.absolutePath">
-          {{ file.name }}
-          <i class="fas fa-download-alt"></i>
-        </a>
-      </div>
-    </div>
-  </panel-component>
-
-  <a
-    *ngFor="let ref of dataset.kgReference"
-    [href]="'https://doi.org/' + ref"
-    target="_blank">
-    Open in Knowledge Graph
-    <i class = "fas fa-new-window"></i>
-  </a>
-
-</div>
\ No newline at end of file
diff --git a/src/ui/kgEntryViewer/subjectViewer/subjectViewer.component.ts b/src/ui/kgEntryViewer/subjectViewer/subjectViewer.component.ts
deleted file mode 100644
index 6b0a80cd18cd8dfa92517a54125e5fde870d6a2d..0000000000000000000000000000000000000000
--- a/src/ui/kgEntryViewer/subjectViewer/subjectViewer.component.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import { ChangeDetectionStrategy, Component, Input } from "@angular/core";
-
-@Component({
-  selector : 'kg-entry-viewer-subject-viewer',
-  templateUrl : './subjectViewer.template.html',
-  styleUrls : ['./subjectViewer.style.css'],
-  changeDetection : ChangeDetectionStrategy.OnPush,
-})
-
-export class SubjectViewer {
-  @Input() public subjects: any = []
-
-  get isSingle(): boolean {
-    return this.subjects.constructor !== Array
-  }
-
-  get species(): string[] {
-    return this.isSingle
-      ? [this.subjects.children.species.value]
-      : this.subjects.reduce((acc: string[], curr: any) =>
-        acc.findIndex(species => species === curr.children.species.value) >= 0
-          ? acc
-          : acc.concat(curr.children.species.value)
-      , [])
-  }
-
-  get groupBySex() {
-    return this.isSingle
-      ? [{
-        name : this.subjects.children.sex.value,
-        count : 1,
-      }]
-      : this.subjects.reduce((acc: any[], curr) =>
-        acc.findIndex(item => item.name === curr.children.sex.value) >= 0
-          ? acc.map(item => item.name === curr.children.sex.value
-            ? Object.assign({}, item, { count: item.count + 1 })
-            : item)
-          : acc.concat({name: curr.children.sex.value, count: 1})
-      , [])
-  }
-}
diff --git a/src/ui/kgEntryViewer/subjectViewer/subjectViewer.style.css b/src/ui/kgEntryViewer/subjectViewer/subjectViewer.style.css
deleted file mode 100644
index 974639e6c80263ea3c2277365e3bbc06e995db8e..0000000000000000000000000000000000000000
--- a/src/ui/kgEntryViewer/subjectViewer/subjectViewer.style.css
+++ /dev/null
@@ -1,4 +0,0 @@
-div.row:nth-child(even)
-{
-  background-color:rgba(128,128,128,0.2);
-}
\ No newline at end of file
diff --git a/src/ui/kgEntryViewer/subjectViewer/subjectViewer.template.html b/src/ui/kgEntryViewer/subjectViewer/subjectViewer.template.html
deleted file mode 100644
index e295f00d6711c2dd3902289a8ea6e695b471b6b6..0000000000000000000000000000000000000000
--- a/src/ui/kgEntryViewer/subjectViewer/subjectViewer.template.html
+++ /dev/null
@@ -1,32 +0,0 @@
-<div>
-
-  <div class = "row">
-    <div class = "col-xs-4 col-lg-4">
-      Species:
-    </div>
-    <div class = "col-xs-8 col-lg-8">
-      {{ species.join(',') }}
-    </div>
-  </div>
-
-  <div class = "row">
-    <div class = "col-xs-4 col-lg-4">
-      Number of Subjects:
-    </div>
-    <div class = "col-xs-8 col-lg-8">
-      {{ subjects.length }}
-    </div>
-  </div>
-
-  <div class = "row">
-    <div class = "col-xs-4 col-lg-4">
-      Sex:
-    </div>
-    <div class = "col-xs-8 col-lg-8">
-      <div *ngFor = "let sexGroup of groupBySex">
-        {{ sexGroup.name }} (n = {{ sexGroup.count }})
-      </div>
-    </div>
-  </div>
-
-</div>
\ No newline at end of file
diff --git a/src/ui/kgtos/index.ts b/src/ui/kgtos/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d683b099119fbee7cb1f239686943c2fa0923a16
--- /dev/null
+++ b/src/ui/kgtos/index.ts
@@ -0,0 +1,4 @@
+import { InjectionToken } from "@angular/core";
+import { Observable } from "rxjs";
+
+export const TOS_OBS_INJECTION_TOKEN = new InjectionToken<Observable<string>>('TOS_STRING')
diff --git a/src/ui/kgtos/kgtos.component.ts b/src/ui/kgtos/kgtos/kgtos.component.ts
similarity index 66%
rename from src/ui/kgtos/kgtos.component.ts
rename to src/ui/kgtos/kgtos/kgtos.component.ts
index 52a3a4239f2c5a636fa0becdd1306f9621fa8921..8f968d8ee2269f96124a945cd9f7ddf8725b7804 100644
--- a/src/ui/kgtos/kgtos.component.ts
+++ b/src/ui/kgtos/kgtos/kgtos.component.ts
@@ -1,7 +1,6 @@
-import { Component, Inject, InjectionToken, Optional } from "@angular/core";
+import { Component, Inject, Optional } from "@angular/core";
 import { Observable } from "rxjs";
-
-export const TOS_OBS_INJECTION_TOKEN = new InjectionToken<Observable<string>>('TOS_STRING')
+import { TOS_OBS_INJECTION_TOKEN } from "..";
 
 @Component({
   selector: 'kgtos-component',
diff --git a/src/ui/kgtos/kgtos/kgtos.style.css b/src/ui/kgtos/kgtos/kgtos.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/ui/kgtos/kgtos.template.html b/src/ui/kgtos/kgtos/kgtos.template.html
similarity index 100%
rename from src/ui/kgtos/kgtos.template.html
rename to src/ui/kgtos/kgtos/kgtos.template.html
diff --git a/src/ui/kgtos/module.ts b/src/ui/kgtos/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..dea1b4f60392d590f7210bee4df1a609eaaeb900
--- /dev/null
+++ b/src/ui/kgtos/module.ts
@@ -0,0 +1,19 @@
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { ComponentsModule } from "src/components";
+import { KGToS } from "./kgtos/kgtos.component";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    ComponentsModule,
+  ],
+  declarations: [
+    KGToS
+  ],
+  exports: [
+    KGToS
+  ]
+})
+
+export class KgTosModule{}
\ No newline at end of file
diff --git a/src/ui/landmarkUI/landmarkUI.component.ts b/src/ui/landmarkUI/landmarkUI.component.ts
deleted file mode 100644
index 9b4635830d5668b7bf6981c7d87c988b0b88ea3e..0000000000000000000000000000000000000000
--- a/src/ui/landmarkUI/landmarkUI.component.ts
+++ /dev/null
@@ -1,71 +0,0 @@
-import { Component, Input, OnChanges, Output, EventEmitter, ChangeDetectionStrategy, ChangeDetectorRef, AfterContentChecked } from "@angular/core";
-import { IDataEntry } from "src/services/stateStore.service"; 
-import { GetKgSchemaIdFromFullIdPipe } from 'src/ui/databrowserModule/util/getKgSchemaIdFromFullId.pipe'
-import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service";
-import { Observable } from "rxjs";
-import { DS_PREVIEW_URL } from 'src/util/constants'
-
-@Component({
-  selector: 'landmark-ui',
-  templateUrl: './landmarkUI.template.html',
-  styleUrls: [
-    './landmarkUI.style.css'
-  ],
-  changeDetection: ChangeDetectionStrategy.OnPush
-})
-
-export class LandmarkUIComponent implements OnChanges, AfterContentChecked{
-  @Input() name: string
-  @Input() fullId: string
-  @Input() datasets: Partial<IDataEntry>[]
-
-  @Output() relayout: EventEmitter<any> = new EventEmitter()
-
-  private pipe = new GetKgSchemaIdFromFullIdPipe()
-
-  public DS_PREVIEW_URL = DS_PREVIEW_URL
-
-  public previewFilesMap: Map<string, any[]> = new Map()
-  public previewFiles: any[] = []
-
-  handleKgDsPrvUpdate(event: CustomEvent, datasetKgId: string){
-    const { detail } = event
-    const { datasetFiles } = detail
-
-    this.previewFilesMap.set(datasetKgId, datasetFiles)
-
-    this.previewFiles = []
-
-    for (const [datasetKgId, previewFiles] of Array.from(this.previewFilesMap)){
-      for (const singlePreviewFile of previewFiles){
-        this.previewFiles.push({
-          ...singlePreviewFile,
-          datasetKgId
-        })
-      }
-    }
-    this.cdr.markForCheck()
-  }
-
-  public filterCriteria: string
-  ngOnChanges(){
-    this.filterCriteria = null
-    if (!this.fullId) return
-    const [kgSchema, kgId] = this.pipe.transform(this.fullId)
-    this.filterCriteria = JSON.stringify([ `${kgSchema}/${kgId}` ])
-  }
-
-  // TODO need to optimise this. This calls way too frequently.
-  ngAfterContentChecked(){
-    this.relayout.emit()
-  }
-
-  public darktheme$: Observable<boolean>
-
-  constructor(
-    constantService: AtlasViewerConstantsServices,
-    private cdr: ChangeDetectorRef
-  ){
-    this.darktheme$ = constantService.darktheme$
-  }
-}
\ No newline at end of file
diff --git a/src/ui/landmarkUI/landmarkUI.style.css b/src/ui/landmarkUI/landmarkUI.style.css
deleted file mode 100644
index 757b1a7bcba50d2da823755e085849b453bff61a..0000000000000000000000000000000000000000
--- a/src/ui/landmarkUI/landmarkUI.style.css
+++ /dev/null
@@ -1,21 +0,0 @@
-kg-dataset-previewer
-{
-  height: 20em;
-  width: 30em;
-}
-.scroll-snap-container
-{
-  scroll-snap-type: x mandatory;
-  scroll-padding-left: 1em;
-  scroll-padding-right: 1em;
-}
-
-.scroll-snap-container > .scroll-snap-child
-{
-  scroll-snap-align: center;
-}
-
-.w-30em
-{
-  width:30em!important;
-}
\ No newline at end of file
diff --git a/src/ui/landmarkUI/landmarkUI.template.html b/src/ui/landmarkUI/landmarkUI.template.html
deleted file mode 100644
index 46085769ecdd1ea45a56ef7cc7f05a6bf690744f..0000000000000000000000000000000000000000
--- a/src/ui/landmarkUI/landmarkUI.template.html
+++ /dev/null
@@ -1,67 +0,0 @@
-<mat-card>
-  <mat-card-title>
-
-    {{ name }}
-  </mat-card-title>
-  <mat-card-subtitle>
-
-    <i class="fas fa-map-marker-alt"></i>
-    <span>
-      spatial landmark
-    </span>
-  </mat-card-subtitle>
-
-  <div class="scroll-snap-container w-100 d-flex flex-row w-30em overflow-auto" mat-card-image>
-    <div *ngFor="let dsPreview of previewFiles"
-      class="scroll-snap-child w-30em">
-      <kg-dataset-previewer
-        [darkmode]="darktheme$ | async"
-        [filename]="dsPreview.filename"
-        [kgId]="dsPreview.datasetKgId"
-        [backendUrl]="DS_PREVIEW_URL">
-      </kg-dataset-previewer>
-    </div>
-
-  </div>
-  <mat-card-content>
-
-    <!-- preview -->
-    <div>
-      <small class="text-muted">
-        Dataset preview
-      </small>
-
-      <ng-container *ngFor="let dataset of datasets; let index = index">
-        <kg-dataset-list
-          class="d-none"
-          [backendUrl]="DS_PREVIEW_URL"
-          [filterCriteriaProp]="filterCriteria"
-          *ngIf="dataset.fullId | getKgSchemaIdFromFullIdPipe as kgSchemaId"
-          (kgDsPrvUpdated)="handleKgDsPrvUpdate($event, kgSchemaId[1])"
-          [kgId]="kgSchemaId[1]">
-
-        </kg-dataset-list>
-      </ng-container>
-    </div>
-
-    <hr class="text-muted">
-    <!-- associated datasets -->
-    <div>
-      <small class="text-muted">
-        Associated datasets
-      </small>
-      <div>
-        <ng-container *ngFor="let dataset of datasets">
-
-          <single-dataset-list-view
-            *ngIf="dataset.fullId | getKgSchemaIdFromFullIdPipe as kgSchemaId"
-            [ripple]="true"
-            [kgSchema]="kgSchemaId[0]"
-            [kgId]="kgSchemaId[1]">
-
-          </single-dataset-list-view>
-        </ng-container>
-      </div>
-    </div>
-  </mat-card-content>
-</mat-card>
diff --git a/src/ui/layerbrowser/layerBrowser.module.ts b/src/ui/layerbrowser/layerBrowser.module.ts
index 241105355b079116e8b8b7e90e54a508690cb813..78a0fbe837a9112e08595381bfe27ddd367e9016 100644
--- a/src/ui/layerbrowser/layerBrowser.module.ts
+++ b/src/ui/layerbrowser/layerBrowser.module.ts
@@ -6,15 +6,16 @@ import {
 import { NgModule } from '@angular/core';
 import { CommonModule } from '@angular/common';
 import { AngularMaterialModule } from '../sharedModules/angularMaterial.module';
-import { GetFilenamePipe } from './getFilename.pipe';
 import { LayerDetailComponent } from './layerDetail/layerDetail.component';
 import { FormsModule } from '@angular/forms';
+import { UtilModule } from 'src/util';
 
 @NgModule({
   imports: [
     CommonModule,
     FormsModule,
-    AngularMaterialModule
+    AngularMaterialModule,
+    UtilModule,
   ],
   declarations: [
     LayerBrowser,
@@ -22,7 +23,6 @@ import { FormsModule } from '@angular/forms';
 
     GetInitialLayerOpacityPipe,
     LockedLayerBtnClsPipe,
-    GetFilenamePipe
   ],
   exports: [
     GetInitialLayerOpacityPipe,
diff --git a/src/ui/layerbrowser/layerBrowserComponent/layerbrowser.component.ts b/src/ui/layerbrowser/layerBrowserComponent/layerbrowser.component.ts
index 4641e4ca4f72f187ddfd27771a50d74a4f5d06df..72a0687e3c67ec3cd078aed5a6bb9d309b9445f4 100644
--- a/src/ui/layerbrowser/layerBrowserComponent/layerbrowser.component.ts
+++ b/src/ui/layerbrowser/layerBrowserComponent/layerbrowser.component.ts
@@ -53,7 +53,7 @@ export class LayerBrowser implements OnInit, OnDestroy {
 
   constructor(
     private store: Store<any>,
-    private constantsService: PureContantService,
+    private pureConstantSvc: PureContantService,
     private log: LoggingService,
   ) {
     this.ngLayers$ = store.pipe(
@@ -109,7 +109,7 @@ export class LayerBrowser implements OnInit, OnDestroy {
       startWith(false)
     )
 
-    this.darktheme$ = this.constantsService.darktheme$.pipe(
+    this.darktheme$ = this.pureConstantSvc.darktheme$.pipe(
       shareReplay(1),
     )
 
diff --git a/src/ui/logoContainer/logoContainer.component.ts b/src/ui/logoContainer/logoContainer.component.ts
index 6af1710795ec7efa9d2a9b8347e38b0e4c3269f2..b7a85fda32a4147929b64e02be4f72c8481066b4 100644
--- a/src/ui/logoContainer/logoContainer.component.ts
+++ b/src/ui/logoContainer/logoContainer.component.ts
@@ -1,5 +1,4 @@
 import { Component } from "@angular/core";
-import { BACKENDURL } from "src/util/constants";
 import { PureContantService } from "src/util";
 import { Subscription } from "rxjs";
 
@@ -13,20 +12,20 @@ import { Subscription } from "rxjs";
 
 export class LogoContainer {
   // only used to define size
-  public imgSrc = `${BACKENDURL}logo`
+  public imgSrc = `${this.pureConstantService.backendUrl}logo`
   
   public containerStyle = {
-    backgroundImage: `url('${BACKENDURL}logo')`
+    backgroundImage: `url('${this.pureConstantService.backendUrl}logo')`
   }
 
   private subscriptions: Subscription[] = []
   constructor(
-    pureConstantService: PureContantService
+    private pureConstantService: PureContantService
   ){
     this.subscriptions.push(
       pureConstantService.darktheme$.subscribe(flag => {
         this.containerStyle = {
-          backgroundImage: `url('${BACKENDURL}logo${!!flag ? '?darktheme=true' : ''}')`
+          backgroundImage: `url('${this.pureConstantService.backendUrl}logo${!!flag ? '?darktheme=true' : ''}')`
         }
       })
     )
diff --git a/src/ui/nehubaContainer/2dLandmarks/landmark.base.ts b/src/ui/nehubaContainer/2dLandmarks/landmark.base.ts
index 52144fb7a41476a6387914067062fd6af99a3653..9d424edba5344b152eb561d4f8e3b92a0414925e 100644
--- a/src/ui/nehubaContainer/2dLandmarks/landmark.base.ts
+++ b/src/ui/nehubaContainer/2dLandmarks/landmark.base.ts
@@ -4,4 +4,6 @@ export class LandmarkUnitBase{
   @Input() public positionX: number = 0
   @Input() public positionY: number = 0
   @Input() public positionZ: number = 0
+
+  @Input() public color: number[] = [255, 255, 255]
 }
diff --git a/src/ui/nehubaContainer/nehuba.module.ts b/src/ui/nehubaContainer/nehuba.module.ts
deleted file mode 100644
index 0104d0c011daa059f725a9b218abfac7f3b494cb..0000000000000000000000000000000000000000
--- a/src/ui/nehubaContainer/nehuba.module.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import { NgModule } from "@angular/core";
-import { NehubaViewerContainerDirective } from './nehubaViewerInterface/nehubaViewerInterface.directive'
-import { NehubaViewerUnit } from "./nehubaViewer/nehubaViewer.component";
-import { CommonModule } from "@angular/common";
-@NgModule({
-  imports: [
-    CommonModule
-  ],
-  declarations: [
-    NehubaViewerContainerDirective,
-    NehubaViewerUnit
-  ],
-  exports: [
-    NehubaViewerContainerDirective,
-    NehubaViewerUnit
-  ],
-  entryComponents: [
-    NehubaViewerUnit
-  ]
-})
-
-export class NehubaModule{}
diff --git a/src/ui/nehubaContainer/nehubaContainer.component.spec.ts b/src/ui/nehubaContainer/nehubaContainer.component.spec.ts
deleted file mode 100644
index 7ef9905515f98d3f3316d968f403ae95319a20db..0000000000000000000000000000000000000000
--- a/src/ui/nehubaContainer/nehubaContainer.component.spec.ts
+++ /dev/null
@@ -1,701 +0,0 @@
-import { CUSTOM_ELEMENTS_SCHEMA, DebugElement } from '@angular/core'
-import { TestBed, async, ComponentFixture, fakeAsync, tick, flush, discardPeriodicTasks } from "@angular/core/testing"
-import { NehubaContainer } from "./nehubaContainer.component"
-import { provideMockStore, MockStore } from "@ngrx/store/testing"
-import { defaultRootState } from 'src/services/stateStore.service'
-import { AngularMaterialModule } from "../sharedModules/angularMaterial.module"
-import { TouchSideClass } from "./touchSideClass.directive"
-import { MaximmisePanelButton } from "./maximisePanelButton/maximisePanelButton.component"
-import { LayoutModule } from 'src/layouts/layout.module'
-import { PureContantService, UtilModule } from "src/util"
-import { AtlasLayerSelector } from "../atlasLayerSelector/atlasLayerSelector.component"
-import { StatusCardComponent } from './statusCard/statusCard.component'
-import { NehubaViewerTouchDirective } from './nehubaViewerInterface/nehubaViewerTouch.directive'
-import { MobileOverlay } from "./mobileOverlay/mobileOverlay.component"
-import { RegionMenuComponent } from "../parcellationRegion/regionMenu/regionMenu.component"
-import { DatabrowserModule } from "../databrowserModule"
-import { SplashScreen } from "./splashScreen/splashScreen.component"
-import { CurrentLayout } from 'src/ui/config/currentLayout/currentLayout.component'
-import { RegionDirective } from 'src/ui/parcellationRegion/region.directive'
-import { RegionTextSearchAutocomplete } from "../viewerStateController/regionSearch/regionSearch.component"
-import { MobileControlNubStylePipe } from './pipes/mobileControlNubStyle.pipe'
-import { ReorderPanelIndexPipe } from './reorderPanelIndex.pipe'
-import { AuthModule } from 'src/auth'
-import { StateModule } from 'src/state'
-import { ReactiveFormsModule, FormsModule } from '@angular/forms'
-import { HttpClientModule } from '@angular/common/http'
-import { WidgetModule } from 'src/widget'
-import { NehubaModule } from './nehuba.module'
-import { CommonModule } from '@angular/common'
-import { IMPORT_NEHUBA_INJECT_TOKEN } from './nehubaViewer/nehubaViewer.component'
-import { viewerStateCustomLandmarkSelector, viewerStateHelperStoreName } from 'src/services/state/viewerState.store.helper'
-import { RenderViewOriginDatasetLabelPipe } from '../parcellationRegion/region.base'
-import { By } from '@angular/platform-browser'
-import { ARIA_LABELS } from 'common/constants'
-import { NoopAnimationsModule } from '@angular/platform-browser/animations'
-import { RegionAccordionTooltipTextPipe } from '../util'
-import { hot } from 'jasmine-marbles'
-import { HttpClientTestingModule } from '@angular/common/http/testing'
-import { ngViewerSelectorOctantRemoval, ngViewerSelectorPanelMode, ngViewerSelectorPanelOrder } from 'src/services/state/ngViewerState/selectors'
-import { PANELS } from 'src/services/state/ngViewerState/constants'
-import { RegionalFeaturesModule } from '../regionalFeatures'
-import { Landmark2DModule } from './2dLandmarks/module'
-import { ViewerCtrlModule } from 'src/viewerCtrl'
-
-const { 
-  TOGGLE_SIDE_PANEL,
-  EXPAND,
-  COLLAPSE,
-  ZOOM_IN,
-  ZOOM_OUT,
-  TOGGLE_FRONTAL_OCTANT
-} = ARIA_LABELS
-
-const _bigbrainJson = require('!json-loader!src/res/ext/bigbrain.json')
-const _bigbrainNehubaConfigJson = require('!json-loader!src/res/ext/bigbrainNehubaConfig.json')
-const bigbrainJson = {
-  ..._bigbrainJson,
-  nehubaConfig: _bigbrainNehubaConfigJson
-}
-const humanAtlas = require('!json-loader!src/res/ext/atlas/atlas_multiLevelHuman.json')
-const importNehubaSpy = jasmine.createSpy('importNehubaSpy').and.returnValue(Promise.reject())
-
-describe('> nehubaContainer.component.ts', () => {
-
-  describe('> NehubaContainer', () => {
-
-    beforeEach(async(() => {
-
-      TestBed.configureTestingModule({
-        imports: [
-          NoopAnimationsModule,
-          WidgetModule,
-          AngularMaterialModule,
-          LayoutModule,
-          UtilModule,
-          DatabrowserModule,
-          NehubaModule,
-          AuthModule,
-          StateModule,
-          FormsModule,
-          ReactiveFormsModule,
-          HttpClientModule,
-          CommonModule,
-          RegionalFeaturesModule,
-
-          /**
-           * because the change done to pureconstant service, need to intercept http call to avoid crypto error message
-           * so and so components needs to be compiled first. make sure you call compileComponents
-           */
-          HttpClientTestingModule,
-          Landmark2DModule,
-          ViewerCtrlModule,
-        ],
-        declarations: [
-          NehubaContainer,
-          TouchSideClass,
-          MaximmisePanelButton,
-          AtlasLayerSelector,
-          StatusCardComponent,
-          NehubaViewerTouchDirective,
-          MobileOverlay,
-          RegionMenuComponent,
-          SplashScreen,
-          CurrentLayout,
-          RegionDirective,
-          RegionTextSearchAutocomplete,
-  
-          // pipes
-          MobileControlNubStylePipe,
-          ReorderPanelIndexPipe,
-          RenderViewOriginDatasetLabelPipe,
-          RegionAccordionTooltipTextPipe,
-        ],
-        providers: [
-          provideMockStore({ initialState: defaultRootState }),
-          {
-            provide: IMPORT_NEHUBA_INJECT_TOKEN,
-            useValue: importNehubaSpy
-          },
-          PureContantService,
-        ],
-        schemas: [
-          CUSTOM_ELEMENTS_SCHEMA
-        ],
-      }).compileComponents()
-      
-    }))
-
-    it('> component can be created', () => {
-      const fixture = TestBed.createComponent(NehubaContainer)
-      fixture.componentInstance.currentOnHoverObs$ = hot('')
-      const el = fixture.debugElement.componentInstance
-      expect(el).toBeTruthy()
-    })
-
-    describe('> on selectedTemplatechange', () => {
-      it('> calls importNehubaPr', async () => {
-        const fixture = TestBed.createComponent(NehubaContainer)
-        fixture.componentInstance.currentOnHoverObs$ = hot('')
-
-        const mockStore = TestBed.inject(MockStore)
-        const newState = {
-          ...defaultRootState,
-          viewerState: {
-            ...defaultRootState.viewerState,
-            fetchedTemplates: [ bigbrainJson ],
-            templateSelected: bigbrainJson,
-            parcellationSelected: bigbrainJson.parcellations[0]
-          },
-          [viewerStateHelperStoreName]: {
-            fetchedAtlases: [ humanAtlas ],
-            selectedAtlasId: humanAtlas['@id']
-          }
-        }
-
-        mockStore.setState(newState)
-        fixture.detectChanges()
-        expect(importNehubaSpy).toHaveBeenCalled()
-      })
-
-      /**
-       * TODO perhaps move this to e2e?
-       */
-      it('> drag handle reattaches properly')
-    })
-
-    describe('> on selectedparcellation change', () => {
-      it('> should set ngId of nehubaViewer', () => {
-        
-        const fixture = TestBed.createComponent(NehubaContainer)
-        fixture.componentInstance.currentOnHoverObs$ = hot('')
-        const el = fixture.debugElement.componentInstance as NehubaContainer
-        const mockStore = TestBed.inject(MockStore)
-        const newState = {
-          ...defaultRootState,
-          viewerState: {
-            ...defaultRootState.viewerState,
-            fetchedTemplates: [ bigbrainJson ],
-            templateSelected: bigbrainJson,
-            parcellationSelected: bigbrainJson.parcellations[0]
-          },
-          [viewerStateHelperStoreName]: {
-            fetchedAtlases: [ humanAtlas ],
-            selectedAtlasId: humanAtlas['@id']
-          }
-        }
-
-        mockStore.setState(newState)
-        fixture.detectChanges()
-
-        const setSpy = spyOnProperty(el.nehubaViewer, 'ngIds', 'set')
-
-        const newState2 = {
-          ...defaultRootState,
-          viewerState: {
-            ...defaultRootState.viewerState,
-            fetchedTemplates: [ bigbrainJson ],
-            templateSelected: bigbrainJson,
-            parcellationSelected: bigbrainJson.parcellations[1]
-          },
-          [viewerStateHelperStoreName]: {
-            fetchedAtlases: [ humanAtlas ],
-            selectedAtlasId: humanAtlas['@id']
-          }
-        }
-
-        mockStore.setState(newState2)
-        fixture.detectChanges()
-
-        expect(setSpy).toHaveBeenCalled()
-        
-      })
-    })
-
-    describe('> extended sidepanel hides and shows as expected', () => {
-      describe('> on start, if nothing is selected', () => {
-        beforeEach(() => {
-          const mockStore = TestBed.inject(MockStore)
-          const newState = {
-            ...defaultRootState,
-            viewerState: {
-              ...defaultRootState.viewerState,
-              fetchedTemplates: [ bigbrainJson ],
-              templateSelected: bigbrainJson,
-              parcellationSelected: bigbrainJson.parcellations[0]
-            },
-            [viewerStateHelperStoreName]: {
-              fetchedAtlases: [ humanAtlas ],
-              selectedAtlasId: humanAtlas['@id']
-            }
-          }
-
-          mockStore.setState(newState)
-        })
-
-        it('> both should be shut', () => {
-          const fixture = TestBed.createComponent(NehubaContainer)
-          fixture.componentInstance.currentOnHoverObs$ = hot('')
-          fixture.detectChanges()
-          expect(
-            fixture.componentInstance.matDrawerMain.opened
-          ).toEqual(false)
-          expect(
-            fixture.componentInstance.matDrawerMinor.opened
-          ).toEqual(false)
-        })
-
-        it('> opening via tab should result in only top drawer open', () => {
-
-          const fixture = TestBed.createComponent(NehubaContainer)
-          fixture.componentInstance.currentOnHoverObs$ = hot('')
-          fixture.detectChanges()
-          const toggleBtn = fixture.debugElement.query( By.css(`[aria-label="${TOGGLE_SIDE_PANEL}"]`) )
-          toggleBtn.triggerEventHandler('click', null)
-          fixture.detectChanges()
-          
-          expect(
-            fixture.componentInstance.matDrawerMain.opened
-          ).toEqual(true)
-          expect(
-            fixture.componentInstance.matDrawerMinor.opened
-          ).toEqual(false)
-        })
-
-        it('> on opening top drawer, explore features should not be present', () => {
-
-          const fixture = TestBed.createComponent(NehubaContainer)
-          fixture.componentInstance.currentOnHoverObs$ = hot('')
-          fixture.detectChanges()
-          const toggleBtn = fixture.debugElement.query( By.css(`[aria-label="${TOGGLE_SIDE_PANEL}"]`) )
-          toggleBtn.triggerEventHandler('click', null)
-          fixture.detectChanges()
-          const expandRegionFeatureBtn = fixture.debugElement.query( By.css(`mat-drawer[data-mat-drawer-secondary-open="true"] [aria-label="${EXPAND}"]`) )
-          expect(expandRegionFeatureBtn).toBeNull()
-        })
-        it('> collapse btn should not be visible', () => {
-
-          const fixture = TestBed.createComponent(NehubaContainer)
-          fixture.componentInstance.currentOnHoverObs$ = hot('')
-          fixture.detectChanges()
-          const toggleBtn = fixture.debugElement.query( By.css(`[aria-label="${TOGGLE_SIDE_PANEL}"]`) )
-          toggleBtn.triggerEventHandler('click', null)
-          fixture.detectChanges()
-          const expandRegionFeatureBtn = fixture.debugElement.query( By.css(`mat-drawer[data-mat-drawer-secondary-open="true"] [aria-label="${COLLAPSE}"]`) )
-          expect(expandRegionFeatureBtn).toBeNull()
-        })
-      })
-
-      describe('> on start, if something is selected', () => {
-        beforeEach(() => {
-          const mockStore = TestBed.inject(MockStore)
-          const newState = {
-            ...defaultRootState,
-            viewerState: {
-              ...defaultRootState.viewerState,
-              fetchedTemplates: [ bigbrainJson ],
-              templateSelected: bigbrainJson,
-              parcellationSelected: bigbrainJson.parcellations[0],
-              regionsSelected: [{
-                name: "foobar"
-              }]
-            },
-            [viewerStateHelperStoreName]: {
-              fetchedAtlases: [ humanAtlas ],
-              selectedAtlasId: humanAtlas['@id']
-            }
-          }
-
-          mockStore.setState(newState)
-        })
-        it('> both should be open', () => {
-          const fixture = TestBed.createComponent(NehubaContainer)
-          fixture.componentInstance.currentOnHoverObs$ = hot('')
-          fixture.detectChanges()
-          expect(
-            fixture.componentInstance.matDrawerMain.opened
-          ).toEqual(true)
-          expect(
-            fixture.componentInstance.matDrawerMinor.opened
-          ).toEqual(true)
-
-          expect(
-            fixture.componentInstance.navSideDrawerMainSwitch.switchState
-          ).toEqual(true)
-          expect(
-            fixture.componentInstance.navSideDrawerMinorSwitch.switchState
-          ).toEqual(true)
-        })
-
-        it('> closing main drawer via tag should close both', () => {
-          const fixture = TestBed.createComponent(NehubaContainer)
-          fixture.componentInstance.currentOnHoverObs$ = hot('')
-          fixture.detectChanges()
-          const toggleBtn = fixture.debugElement.query( By.css(`[aria-label="${TOGGLE_SIDE_PANEL}"]`) )
-          toggleBtn.triggerEventHandler('click', null)
-          fixture.detectChanges()
-          expect(
-            fixture.componentInstance.matDrawerMain.opened
-          ).toEqual(false)
-
-          /**
-           * TODO investigate why openedStart/closedStart events fail to fire
-           */
-          // expect(
-          //   fixture.componentInstance.matDrawerMinor.opened
-          // ).toEqual(false)
-
-          // expect(
-          //   fixture.componentInstance.navSideDrawerMainSwitch.switchState
-          // ).toEqual(false)
-          // expect(
-          //   fixture.componentInstance.navSideDrawerMinorSwitch.switchState
-          // ).toEqual(false)
-        })
-        it('> collapse btn should be visible', () => {
-
-          const fixture = TestBed.createComponent(NehubaContainer)
-          fixture.componentInstance.currentOnHoverObs$ = hot('')
-          fixture.detectChanges()
-          const collapseRegionFeatureBtn = fixture.debugElement.query( By.css(`mat-drawer[data-mat-drawer-secondary-open="true"] [aria-label="${COLLAPSE}"]`) )
-          expect(collapseRegionFeatureBtn).not.toBeNull()
-        })
-        it('> clicking on collapse btn should minimize 1 drawer', () => {
-
-          const fixture = TestBed.createComponent(NehubaContainer)
-          fixture.componentInstance.currentOnHoverObs$ = hot('')
-          fixture.detectChanges()
-          const collapseRegionFeatureBtn = fixture.debugElement.query( By.css(`mat-drawer[data-mat-drawer-secondary-open="true"] [aria-label="${COLLAPSE}"]`) )
-          collapseRegionFeatureBtn.triggerEventHandler('click', null)
-          fixture.detectChanges()
-          expect(
-            fixture.componentInstance.matDrawerMain.opened
-          ).toEqual(true)
-
-          /**
-           * TODO investigate why property does not get updated
-           */
-          // expect(
-          //   fixture.componentInstance.matDrawerMinor.opened
-          // ).toEqual(false)
-
-          expect(
-            fixture.componentInstance.navSideDrawerMainSwitch.switchState
-          ).toEqual(true)
-          expect(
-            fixture.componentInstance.navSideDrawerMinorSwitch.switchState
-          ).toEqual(false)
-        })
-
-        it('> on minimize drawer, clicking expand btn should expand everything', () => {
-          const fixture = TestBed.createComponent(NehubaContainer)
-          fixture.componentInstance.currentOnHoverObs$ = hot('')
-          fixture.detectChanges()
-          const collapseRegionFeatureBtn = fixture.debugElement.query( By.css(`mat-drawer[data-mat-drawer-secondary-open="true"] [aria-label="${COLLAPSE}"]`) )
-          collapseRegionFeatureBtn.triggerEventHandler('click', null)
-          fixture.detectChanges()
-          const expandRegionFeatureBtn = fixture.debugElement.query( By.css(`mat-drawer[data-mat-drawer-primary-open="true"] [aria-label="${EXPAND}"]`) )
-          expandRegionFeatureBtn.triggerEventHandler('click', null)
-          fixture.detectChanges()
-
-          expect(
-            fixture.componentInstance.matDrawerMain.opened
-          ).toEqual(true)
-          expect(
-            fixture.componentInstance.matDrawerMinor.opened
-          ).toEqual(true)
-
-          expect(
-            fixture.componentInstance.navSideDrawerMainSwitch.switchState
-          ).toEqual(true)
-          /**
-           * TODO figoure out why switch state is updated async, and karma can't force update state
-           */
-          // expect(
-          //   fixture.componentInstance.navSideDrawerMinorSwitch.switchState
-          // ).toEqual(true)
-        })
-      })
-
-      describe('> side bar content', () => {
-
-        /**
-         * TODO
-         */
-        it('> if nothing is shown, it should show place holder text')
-
-        /**
-         * TODO
-         */
-        it('> if something (region features/connectivity) exists, placeh holder text should be hdiden')
-      })
-    })
-  
-    describe('> panelCtrl', () => {
-      let fixture: ComponentFixture<NehubaContainer>
-      const setViewerLoaded = () => {
-        fixture.componentInstance.viewerLoaded = true
-      }
-      const ctrlElementIsVisible = (el: DebugElement) => {
-        const visible = (el.nativeElement as HTMLElement).getAttribute('data-viewer-controller-visible')
-        return visible === 'true'
-      }
-      beforeEach(() => {
-        fixture = TestBed.createComponent(NehubaContainer)
-      })
-      it('> on start, all four ctrl panels exists', () => {
-        fixture.detectChanges()
-        setViewerLoaded()
-        fixture.detectChanges()
-        for (const idx of [0, 1, 2, 3]) {
-          const el = fixture.debugElement.query(
-            By.css(`[data-viewer-controller-index="${idx}"]`)
-          )
-          expect(el).toBeTruthy()
-        }
-      })
-
-      it('> on start all four ctrl panels are invisible', () => {
-        
-        fixture.detectChanges()
-        setViewerLoaded()
-        fixture.detectChanges()
-        for (const idx of [0, 1, 2, 3]) {
-          const el = fixture.debugElement.query(
-            By.css(`[data-viewer-controller-index="${idx}"]`)
-          )
-          expect(ctrlElementIsVisible(el)).toBeFalsy()
-        }
-      })
-
-      describe('> on hover, only the hovered panel have ctrl shown', () => {
-
-        for (const idx of [0, 1, 2, 3]) {
-          
-          it(`> on hoveredPanelIndices$ emit ${idx}, the panel index ${idx} ctrl becomes visible`, fakeAsync(() => {
-            fixture.detectChanges()
-            const findPanelIndexSpy = spyOn<any>(fixture.componentInstance, 'findPanelIndex').and.callFake(() => {
-              return idx
-            })
-            setViewerLoaded()
-            fixture.detectChanges()
-            const nativeElement = fixture.componentInstance['elementRef'].nativeElement
-            nativeElement.dispatchEvent(new MouseEvent('mouseover', { bubbles: true }))
-  
-            /**
-             * assert findPanelIndex called with event.target, i.e. native element in thsi case
-             */
-            expect(findPanelIndexSpy).toHaveBeenCalledWith(nativeElement)
-            tick(200)
-            fixture.detectChanges()
-            
-            /**
-             * every panel index should be non visible
-             * only when idx matches, it can be visible
-             * n.b. this does not test visual visibility (which is controlled by extra-style.css)
-             * (which is also affected by global [ismobile] configuration)
-             * 
-             * this merely test the unit logic, and sets the flag appropriately
-             */
-            for (const iterativeIdx of [0, 1, 2, 3]) {
-              const el = fixture.debugElement.query(
-                By.css(`[data-viewer-controller-index="${iterativeIdx}"]`)
-              )
-              if (iterativeIdx === idx) {
-                expect(ctrlElementIsVisible(el)).toBeTruthy()
-              } else {
-                expect(ctrlElementIsVisible(el)).toBeFalsy()
-              }
-            }
-            discardPeriodicTasks()
-          }))
-        }
-  
-      })
-
-      describe('> on maximise top right slice panel (idx 1)', () => {
-        beforeEach(() => {
-          const mockStore = TestBed.inject(MockStore)
-          mockStore.overrideSelector(ngViewerSelectorPanelMode, PANELS.SINGLE_PANEL)
-          mockStore.overrideSelector(ngViewerSelectorPanelOrder, '1230')
-
-          fixture.detectChanges()
-          setViewerLoaded()
-          fixture.detectChanges()
-        })
-        it('> toggle front octant btn not visible', () => {
-
-          const toggleBtn = fixture.debugElement.query(
-            By.css(`[cell-i] [aria-label="${TOGGLE_FRONTAL_OCTANT}"]`)
-          )
-          expect(toggleBtn).toBeFalsy()
-        })
-
-        it('> zoom in and out btns are visible', () => {
-
-          const zoomInBtn = fixture.debugElement.query(
-            By.css(`[cell-i] [aria-label="${ZOOM_IN}"]`)
-          )
-
-          const zoomOutBtn = fixture.debugElement.query(
-            By.css(`[cell-i] [aria-label="${ZOOM_OUT}"]`)
-          )
-
-          expect(zoomInBtn).toBeTruthy()
-          expect(zoomOutBtn).toBeTruthy()
-        })
-
-        it('> zoom in btn calls fn with right param', () => {
-          const zoomViewSpy = spyOn(fixture.componentInstance, 'zoomNgView')
-
-          const zoomInBtn = fixture.debugElement.query(
-            By.css(`[cell-i] [aria-label="${ZOOM_IN}"]`)
-          )
-          zoomInBtn.triggerEventHandler('click', null)
-          expect(zoomViewSpy).toHaveBeenCalled()
-          const { args } = zoomViewSpy.calls.first()
-          expect(args[0]).toEqual(1)
-          /**
-           * zoom in < 1
-           */
-          expect(args[1]).toBeLessThan(1)
-        })
-        it('> zoom out btn calls fn with right param', () => {
-          const zoomViewSpy = spyOn(fixture.componentInstance, 'zoomNgView')
-
-          const zoomOutBtn = fixture.debugElement.query(
-            By.css(`[cell-i] [aria-label="${ZOOM_OUT}"]`)
-          )
-          zoomOutBtn.triggerEventHandler('click', null)
-          expect(zoomViewSpy).toHaveBeenCalled()
-          const { args } = zoomViewSpy.calls.first()
-          expect(args[0]).toEqual(1)
-          /**
-           * zoom out > 1
-           */
-          expect(args[1]).toBeGreaterThan(1)
-        })
-      })
-      describe('> on maximise perspective panel', () => {
-        beforeEach(() => {
-          const mockStore = TestBed.inject(MockStore)
-          mockStore.overrideSelector(ngViewerSelectorPanelMode, PANELS.SINGLE_PANEL)
-          mockStore.overrideSelector(ngViewerSelectorPanelOrder, '3012')
-
-          fixture.detectChanges()
-          setViewerLoaded()
-          fixture.detectChanges()
-        })
-        it('> toggle octant btn visible and functional', () => {
-          const setOctantRemovalSpy = spyOn(fixture.componentInstance, 'setOctantRemoval')
-
-          const toggleBtn = fixture.debugElement.query(
-            By.css(`[cell-i] [aria-label="${TOGGLE_FRONTAL_OCTANT}"]`)
-          )
-          expect(toggleBtn).toBeTruthy()
-          toggleBtn.nativeElement.dispatchEvent(
-            new MouseEvent('click', { bubbles: true })
-          )
-          expect(setOctantRemovalSpy).toHaveBeenCalled()
-        })
-
-        it('> zoom in btn visible and functional', () => {
-          const zoomViewSpy = spyOn(fixture.componentInstance, 'zoomNgView')
-
-          const zoomInBtn = fixture.debugElement.query(
-            By.css(`[cell-i] [aria-label="${ZOOM_IN}"]`)
-          )
-          expect(zoomInBtn).toBeTruthy()
-
-          zoomInBtn.triggerEventHandler('click', null)
-          expect(zoomViewSpy).toHaveBeenCalled()
-          const { args } = zoomViewSpy.calls.first()
-          expect(args[0]).toEqual(3)
-          /**
-           * zoom in < 1
-           */
-          expect(args[1]).toBeLessThan(1)
-        })
-        it('> zoom out btn visible and functional', () => {
-          const zoomViewSpy = spyOn(fixture.componentInstance, 'zoomNgView')
-
-          const zoomOutBtn = fixture.debugElement.query(
-            By.css(`[cell-i] [aria-label="${ZOOM_OUT}"]`)
-          )
-          expect(zoomOutBtn).toBeTruthy()
-
-          zoomOutBtn.triggerEventHandler('click', null)
-          expect(zoomViewSpy).toHaveBeenCalled()
-          const { args } = zoomViewSpy.calls.first()
-          expect(args[0]).toEqual(3)
-          /**
-           * zoom in < 1
-           */
-          expect(args[1]).toBeGreaterThan(1)
-        })
-      
-      })
-    })
-  
-    describe('> on userLandmarks change', () => {
-      const lm1 = {
-        id: 'test-1',
-        position: [0, 0, 0]
-      }
-      const lm2 = {
-        id: 'test-2',
-        position: [1, 1,1 ]
-      }
-      it('> calls nehubaViewer.updateUserLandmarks', () => {
-        const fixture = TestBed.createComponent(NehubaContainer)
-
-        fixture.componentInstance.nehubaViewer = {
-          updateUserLandmarks: () => {}
-        } as any
-
-        const updateUserLandmarksSpy = spyOn(
-          fixture.componentInstance.nehubaViewer,
-          'updateUserLandmarks'
-        )
-
-        const mockStore = TestBed.inject(MockStore)
-        mockStore.overrideSelector(viewerStateCustomLandmarkSelector, [
-          lm1, 
-          lm2
-        ])
-        fixture.detectChanges()
-        expect(
-          updateUserLandmarksSpy
-        ).toHaveBeenCalledWith([
-          lm1, lm2
-        ])
-      })
-    
-      it('> calls togglecotantREmoval', () => {
-        
-        const fixture = TestBed.createComponent(NehubaContainer)
-
-        fixture.componentInstance.nehubaContainerDirective = {
-          toggleOctantRemoval: () => {},
-          clear: () => {}
-        } as any
-
-        const toggleOctantRemovalSpy = spyOn(
-          fixture.componentInstance.nehubaContainerDirective,
-          'toggleOctantRemoval'
-        )
-
-        const mockStore = TestBed.inject(MockStore)
-        mockStore.overrideSelector(viewerStateCustomLandmarkSelector, [
-          lm1, 
-          lm2
-        ])
-        mockStore.overrideSelector(ngViewerSelectorOctantRemoval, true)
-        fixture.detectChanges()
-        expect(
-          toggleOctantRemovalSpy
-        ).toHaveBeenCalledWith(false)
-      })
-    })
-  })
-})
diff --git a/src/ui/nehubaContainer/nehubaContainer.component.ts b/src/ui/nehubaContainer/nehubaContainer.component.ts
deleted file mode 100644
index 31baa10763f7dfb2bffb6266a62a982c57b56131..0000000000000000000000000000000000000000
--- a/src/ui/nehubaContainer/nehubaContainer.component.ts
+++ /dev/null
@@ -1,1183 +0,0 @@
-import { Component, ElementRef, Input, OnChanges, OnDestroy, OnInit, ViewChild, ChangeDetectorRef, Output, EventEmitter, Inject, Optional } from "@angular/core";
-import { select, Store } from "@ngrx/store";
-import { combineLatest, fromEvent, merge, Observable, of, Subscription, timer, asyncScheduler, BehaviorSubject, Subject } from "rxjs";
-import { buffer, debounceTime, distinctUntilChanged, filter, map, mapTo, scan, shareReplay, skip, startWith, switchMap, switchMapTo, take, tap, withLatestFrom, delayWhen, throttleTime } from "rxjs/operators";
-import { trigger, state, style, animate, transition } from '@angular/animations'
-import { MatDrawer } from "@angular/material/sidenav";
-
-import { LoggingService } from "src/logging";
-import {
-  CHANGE_NAVIGATION,
-  generateLabelIndexId,
-  getMultiNgIdsRegionsLabelIndexMap,
-  getNgIds,
-  ILandmark,
-  IOtherLandmarkGeometry,
-  IPlaneLandmarkGeometry,
-  IPointLandmarkGeometry,
-  isDefined,
-  MOUSE_OVER_LANDMARK,
-  NgViewerStateInterface
-} from "src/services/stateStore.service";
-
-import { getExportNehuba, isSame } from "src/util/fn";
-import { API_SERVICE_SET_VIEWER_HANDLE_TOKEN, IUserLandmark } from "src/atlasViewer/atlasViewer.apiService.service";
-import { NehubaViewerUnit } from "./nehubaViewer/nehubaViewer.component";
-import { compareLandmarksChanged } from "src/util/constants";
-import { PureContantService } from "src/util";
-import { ARIA_LABELS, IDS, CONST } from 'common/constants'
-import { ngViewerActionSetPerspOctantRemoval, PANELS, ngViewerActionToggleMax, ngViewerActionAddNgLayer, ngViewerActionRemoveNgLayer } from "src/services/state/ngViewerState.store.helper";
-import { viewerStateSelectRegionWithIdDeprecated, viewerStateAddUserLandmarks, viewreStateRemoveUserLandmarks, viewerStateCustomLandmarkSelector, viewerStateSelectedParcellationSelector, viewerStateSelectedTemplateSelector, viewerStateSelectedRegionsSelector } from 'src/services/state/viewerState.store.helper'
-import { SwitchDirective } from "src/util/directives/switch.directive";
-import {
-  viewerStateDblClickOnViewer,
-} from "src/services/state/viewerState.store.helper";
-
-import { getFourPanel, getHorizontalOneThree, getSinglePanel, getVerticalOneThree, calculateSliceZoomFactor, scanSliceViewRenderFn as scanFn, takeOnePipe } from "./util";
-import { NehubaViewerContainerDirective } from "./nehubaViewerInterface/nehubaViewerInterface.directive";
-import { ITunableProp } from "./mobileOverlay/mobileOverlay.component";
-import {ConnectivityBrowserComponent} from "src/ui/connectivityBrowser/connectivityBrowser.component";
-import { viewerStateMouseOverCustomLandmark } from "src/services/state/viewerState/actions";
-import { ngViewerSelectorNehubaReady, ngViewerSelectorOctantRemoval, ngViewerSelectorPanelMode, ngViewerSelectorPanelOrder } from "src/services/state/ngViewerState/selectors";
-import { REGION_OF_INTEREST } from "src/util/interfaces";
-import { uiActionHideAllDatasets, uiActionHideDatasetWithId } from "src/services/state/uiState/actions";
-
-const { MESH_LOADING_STATUS } = IDS
-
-const sortByFreshness: (acc: any[], curr: any[]) => any[] = (acc, curr) => {
-
-  const getLayerName = ({layer} = {layer: {}}) => {
-    const { name } = layer as any
-    return name
-  }
-
-  const newEntries = (curr && curr.filter(entry => {
-    const name = getLayerName(entry)
-    return acc.map(getLayerName).indexOf(name) < 0
-  })) || []
-
-  const entryChanged: (itemPrevState, newArr) => boolean = (itemPrevState, newArr) => {
-    const layerName = getLayerName(itemPrevState)
-    const { segment } = itemPrevState
-    const foundItem = newArr?.find((_item) =>
-      getLayerName(_item) === layerName)
-
-    if (foundItem) {
-      const { segment: foundSegment } = foundItem
-      return segment !== foundSegment
-    } else {
-      /**
-       * if item was not found in the new array, meaning hovering nothing
-       */
-      return segment !== null
-    }
-  }
-
-  const getItemFromLayerName = (item, arr) => {
-    const foundItem = arr?.find(i => getLayerName(i) === getLayerName(item))
-    return foundItem
-      ? foundItem
-      : {
-        layer: item.layer,
-        segment: null,
-      }
-  }
-
-  const getReduceExistingLayers = (newArr) => ([changed, unchanged], _curr) => {
-    const changedFlag = entryChanged(_curr, newArr)
-    return changedFlag
-      ? [ changed.concat( getItemFromLayerName(_curr, newArr) ), unchanged ]
-      : [ changed, unchanged.concat(_curr) ]
-  }
-
-  /**
-   * now, for all the previous layers, separate into changed and unchanged layers
-   */
-  const [changed, unchanged] = acc.reduce(getReduceExistingLayers(curr), [[], []])
-  return [...newEntries, ...changed, ...unchanged]
-}
-
-const {
-  ZOOM_IN,
-  ZOOM_OUT,
-  TOGGLE_FRONTAL_OCTANT,
-  TOGGLE_SIDE_PANEL,
-  EXPAND,
-  COLLAPSE,
-  ADDITIONAL_VOLUME_CONTROL,
-} = ARIA_LABELS
-
-@Component({
-  selector : 'ui-nehuba-container',
-  templateUrl : './nehubaContainer.template.html',
-  styleUrls : [
-    `./nehubaContainer.style.css`,
-  ],
-  animations: [
-    trigger('openClose', [
-      state('open', style({
-        transform: 'translateY(0)',
-        opacity: 1
-      })),
-      state('closed', style({
-        transform: 'translateY(-100vh)',
-        opacity: 0
-      })),
-      transition('open => closed', [
-        animate('200ms cubic-bezier(0.35, 0, 0.25, 1)')
-      ]),
-      transition('closed => open', [
-        animate('200ms cubic-bezier(0.35, 0, 0.25, 1)')
-      ])
-    ]),
-    trigger('openCloseAnchor', [
-      state('open', style({
-        transform: 'translateY(0)'
-      })),
-      state('closed', style({
-        transform: 'translateY(100vh)'
-      })),
-      transition('open => closed', [
-        animate('200ms cubic-bezier(0.35, 0, 0.25, 1)')
-      ]),
-      transition('closed => open', [
-        animate('200ms cubic-bezier(0.35, 0, 0.25, 1)')
-      ])
-    ]),
-  ],
-  exportAs: 'uiNehubaContainer',
-  providers: [
-    {
-      provide: REGION_OF_INTEREST,
-      useFactory: (store: Store<any>) => store.pipe(
-        select(viewerStateSelectedRegionsSelector),
-        map(rs => rs[0] || null)
-      ),
-      deps: [
-        Store
-      ]
-    }
-  ]
-})
-
-export class NehubaContainer implements OnInit, OnChanges, OnDestroy {
-
-  public CONST = CONST
-  public ARIA_LABEL_ZOOM_IN = ZOOM_IN
-  public ARIA_LABEL_ZOOM_OUT = ZOOM_OUT
-  public ARIA_LABEL_TOGGLE_FRONTAL_OCTANT = TOGGLE_FRONTAL_OCTANT
-  public ARIA_LABEL_TOGGLE_SIDE_PANEL = TOGGLE_SIDE_PANEL
-  public ARIA_LABEL_EXPAND = EXPAND
-  public ARIA_LABEL_COLLAPSE = COLLAPSE
-  public ARIA_LABEL_ADDITIONAL_VOLUME_CONTROL = ADDITIONAL_VOLUME_CONTROL
-  
-  public ID_MESH_LOADING_STATUS = MESH_LOADING_STATUS
-
-  @ViewChild(NehubaViewerContainerDirective,{static: true})
-  public nehubaContainerDirective: NehubaViewerContainerDirective
-
-  @ViewChild('sideNavMasterSwitch', { static: true })
-  public navSideDrawerMainSwitch: SwitchDirective
-  @ViewChild('sideNavSwitch', { static: true })
-  public navSideDrawerMinorSwitch: SwitchDirective
-
-  @ViewChild('matDrawerMaster', {static: true})
-  public matDrawerMain: MatDrawer
-  @ViewChild('matDrawerMinor', { static: true })
-  public matDrawerMinor: MatDrawer
-
-  @Output()
-  public nehubaViewerLoaded: EventEmitter<boolean> = new EventEmitter()
-
-  @Output()
-  public forceUI$: Observable<{ target: 'perspective:octantRemoval', mode: boolean,  message?: string }>
-
-  public disableOctantRemoval$: Observable<{ message?: string, mode: boolean }>
-
-  public handleViewerLoadedEvent(flag: boolean){
-    this.viewerLoaded = flag
-    this.nehubaViewerLoaded.emit(flag)
-  }
-
-  public viewerLoaded: boolean = false
-
-  private sliceRenderEvent$: Observable<CustomEvent>
-  public sliceViewLoadingMain$: Observable<[boolean, boolean, boolean]>
-  public perspectiveViewLoading$: Observable<string|null>
-  public showPerpsectiveScreen$: Observable<string>
-
-  public templateSelected$: Observable<any> = this.store.pipe(
-    select(viewerStateSelectedTemplateSelector),
-    distinctUntilChanged(isSame),
-  )
-
-  private newViewer$: Observable<any> = this.templateSelected$.pipe(
-    filter(v => !!v),
-  )
-
-  private selectedParcellation$: Observable<any> = this.store.pipe(
-    select(viewerStateSelectedParcellationSelector),
-    distinctUntilChanged(),
-    filter(v => !!v)
-  )
-  public selectedRegions: any[] = []
-  public selectedRegions$: Observable<any[]> = this.store.pipe(
-    select(viewerStateSelectedRegionsSelector),
-    filter(rs => !!rs),
-  )
-
-  public selectedLandmarks$: Observable<any[]>
-  public selectedPtLandmarks$: Observable<any[]>
-  public customLandmarks$: Observable<any> = this.store.pipe(
-    select(viewerStateCustomLandmarkSelector),
-    map(lms => lms.map(lm => ({
-      ...lm,
-      geometry: {
-        position: lm.position
-      }
-    })))
-  )
-  private hideSegmentations$: Observable<boolean>
-
-  private fetchedSpatialDatasets$: Observable<ILandmark[]>
-  private userLandmarks$: Observable<IUserLandmark[]>
-
-  public nehubaViewerPerspectiveOctantRemoval$: Observable<boolean>
-
-  @Input()
-  private currentOnHover: {segments: any, landmark: any, userLandmark: any}
-
-  @Input()
-  currentOnHoverObs$: Observable<{segments: any, landmark: any, userLandmark: any}>
-
-  public iavAdditionalLayers$ = new Subject<any[]>()
-
-  public alwaysHideMinorPanel$: Observable<boolean> = combineLatest(
-    this.selectedRegions$,
-    this.iavAdditionalLayers$.pipe(
-      startWith([])
-    )
-  ).pipe(
-    map(([ regions, layers ]) => regions.length === 0 && layers.length === 0)
-  )
-
-  public onHoverSegments$: BehaviorSubject<any[]> = new BehaviorSubject([])
-  public onHoverSegment$: Observable<any> = this.onHoverSegments$.pipe(
-    scan(sortByFreshness, []),
-    /**
-     * take the first element after sort by freshness
-     */
-    map(arr => arr[0]),
-    /**
-     * map to the older interface
-     */
-    filter(v => !!v),
-    map(({ segment }) => {
-      return {
-        labelIndex: (isNaN(segment) && Number(segment.labelIndex)) || null,
-        foundRegion: (isNaN(segment) && segment) || null,
-      }
-    }),
-  )
-
-  public selectedTemplate: any | null
-  private selectedRegionIndexSet: Set<string> = new Set()
-  public fetchedSpatialData: ILandmark[] = []
-
-  private ngLayersRegister: Partial<NgViewerStateInterface> = {layers : [], forceShowSegment: null}
-  private ngLayers$: Observable<NgViewerStateInterface>
-
-  public selectedParcellation: any | null
-
-  public nehubaViewer: NehubaViewerUnit = null
-  private multiNgIdsRegionsLabelIndexMap: Map<string, Map<number, any>> = new Map()
-  private landmarksLabelIndexMap: Map<number, any> = new Map()
-  private landmarksNameMap: Map<string, number> = new Map()
-
-  private subscriptions: Subscription[] = []
-
-  public nanometersToOffsetPixelsFn: Array<(...arg) => any> = []
-
-  public viewPanels: [HTMLElement, HTMLElement, HTMLElement, HTMLElement] = [null, null, null, null]
-  public panelMode$: Observable<string>
-
-  public panelOrder$: Observable<string>
-  private redrawLayout$: Observable<[string, string]>
-
-  public hoveredPanelIndices$: Observable<number>
-
-  public connectivityNumber: string
-  public connectivityLoadUrl: string
-
-  constructor(
-    private pureConstantService: PureContantService,
-    @Optional() @Inject(API_SERVICE_SET_VIEWER_HANDLE_TOKEN) private setViewerHandle: (arg) => void,
-    private store: Store<any>,
-    private elementRef: ElementRef,
-    private log: LoggingService,
-    private cdr: ChangeDetectorRef,
-    @Optional() @Inject(REGION_OF_INTEREST) public regionOfInterest$: Observable<any>
-  ) {
-
-    this.useMobileUI$ = this.pureConstantService.useTouchUI$
-
-    this.nehubaViewerPerspectiveOctantRemoval$ = this.store.pipe(
-      select(ngViewerSelectorOctantRemoval),
-    )
-
-    this.panelMode$ = this.store.pipe(
-      select(ngViewerSelectorPanelMode),
-      distinctUntilChanged(),
-      shareReplay(1),
-    )
-
-    this.panelOrder$ = this.store.pipe(
-      select(ngViewerSelectorPanelOrder),
-      distinctUntilChanged(),
-      shareReplay(1),
-    )
-
-    this.redrawLayout$ = this.store.pipe(
-      select(ngViewerSelectorNehubaReady),
-      distinctUntilChanged(),
-      filter(v => !!v),
-      switchMapTo(combineLatest([
-        this.panelMode$,
-        this.panelOrder$,
-      ])),
-    )
-
-    this.selectedLandmarks$ = this.store.pipe(
-      select('viewerState'),
-      select('landmarksSelected'),
-    )
-
-    this.selectedPtLandmarks$ = this.selectedLandmarks$.pipe(
-      map(lms => lms.filter(lm => lm.geometry.type === 'point')),
-    )
-
-    this.fetchedSpatialDatasets$ = this.store.pipe(
-      select('dataStore'),
-      select('fetchedSpatialData'),
-      distinctUntilChanged(compareLandmarksChanged),
-      filter(v => !!v),
-      startWith([]),
-      debounceTime(300),
-    )
-
-    this.userLandmarks$ = this.store.pipe(
-      select(viewerStateCustomLandmarkSelector),
-      distinctUntilChanged(),
-    )
-
-    /**
-     * in future, perhaps add other force UI optinos here
-     */
-    this.forceUI$ = this.userLandmarks$.pipe(
-      map(lm => {
-        if (lm.length > 0) {
-          return {
-            target: 'perspective:octantRemoval',
-            mode: false,
-            message: `octant control disabled: showing landmarks.`
-          }
-        } else {
-          return {
-            target: 'perspective:octantRemoval',
-            mode: null
-          }
-        }
-      })
-    )
-
-    this.disableOctantRemoval$ = this.forceUI$.pipe(
-      filter(({ target }) => target === 'perspective:octantRemoval'),
-    )
-
-    this.sliceRenderEvent$ = fromEvent(this.elementRef.nativeElement, 'sliceRenderEvent').pipe(
-      map(ev => ev as CustomEvent)
-    )
-
-    this.sliceViewLoadingMain$ = this.sliceRenderEvent$.pipe(
-      scan(scanFn, [null, null, null]),
-      startWith([true, true, true] as [boolean, boolean, boolean]),
-      shareReplay(1),
-    )
-
-    this.showPerpsectiveScreen$ = this.newViewer$.pipe(
-      switchMapTo(this.sliceRenderEvent$.pipe(
-        scan((acc, curr) => {
-
-          /**
-           * if at any point, all chunks have been loaded, always return loaded state
-           */
-          if (acc.every(v => v === 0)) return [0, 0, 0]
-          const { detail = {}, target } = curr || {}
-          const { missingChunks = -1, missingImageChunks = -1 } = detail
-          const idx = this.findPanelIndex(target as HTMLElement)
-          const returnAcc = [...acc]
-          if (idx >= 0) {
-            returnAcc[idx] = missingChunks + missingImageChunks
-          }
-          return returnAcc
-        }, [-1, -1, -1]),
-        map(arr => {
-          let sum = 0
-          let uncertain = false
-          for (const num of arr) {
-            if (num < 0) {
-              uncertain = true
-            } else {
-              sum += num
-            }
-          }
-          return sum > 0
-            ? `Loading ${sum}${uncertain ? '+' : ''} chunks ...`
-            : null
-        }),
-        distinctUntilChanged(),
-        startWith('Loading ...'),
-        throttleTime(100, asyncScheduler, { leading: true, trailing: true }),
-        shareReplay(1),
-      ))
-    )
-
-    /* missing chunk perspective view */
-    this.perspectiveViewLoading$ = fromEvent(this.elementRef.nativeElement, 'perpspectiveRenderEvent')
-      .pipe(
-        filter(event => isDefined(event) && isDefined((event as any).detail) && isDefined((event as any).detail.lastLoadedMeshId) ),
-        map(event => {
-
-          /**
-           * TODO dig into event detail to see if the exact mesh loaded
-           */
-          const { meshesLoaded, meshFragmentsLoaded, lastLoadedMeshId } = (event as any).detail
-          return meshesLoaded >= this.nehubaViewer.numMeshesToBeLoaded
-            ? null
-            : 'Loading meshes ...'
-        }),
-        distinctUntilChanged()
-      )
-
-    this.ngLayers$ = this.store.pipe(
-      select('ngViewerState'),
-    )
-
-    this.hideSegmentations$ = this.ngLayers$.pipe(
-      map(state => isDefined(state)
-        ? state.layers?.findIndex(l => l.mixability === 'nonmixable') >= 0
-        : false),
-    )
-
-    /**
-     * fixes 
-     * https://github.com/HumanBrainProject/interactive-viewer/issues/800
-     */
-    this.subscriptions.push(
-      this.nehubaViewerLoaded.pipe(
-        debounceTime(500),
-        filter(v => !v),
-      ).subscribe(() => {
-        this.matDrawerMain.close()
-        this.matDrawerMinor.close()
-      })
-    )
-  }
-
-  public useMobileUI$: Observable<boolean>
-
-  private removeExistingPanels() {
-    const element = this.nehubaViewer.nehubaViewer.ngviewer.layout.container.componentValue.element as HTMLElement
-    while (element.childElementCount > 0) {
-      element.removeChild(element.firstElementChild)
-    }
-    return element
-  }
-
-  private findPanelIndex = (panel: HTMLElement) => this.viewPanels?.findIndex(p => p === panel)
-
-  private _exportNehuba: any
-  get exportNehuba() {
-    if (!this._exportNehuba) {
-      this._exportNehuba = getExportNehuba()
-    }
-    return this._exportNehuba
-  }
-
-  public ngOnInit() {
-    this.hoveredPanelIndices$ = fromEvent(this.elementRef.nativeElement, 'mouseover').pipe(
-      switchMap((ev: MouseEvent) => merge(
-        of(this.findPanelIndex(ev.target as HTMLElement)),
-        fromEvent(this.elementRef.nativeElement, 'mouseout').pipe(
-          mapTo(null),
-        ),
-      )),
-      debounceTime(20),
-      shareReplay(1),
-    )
-
-    // TODO deprecate
-    /* each time a new viewer is initialised, take the first event to get the translation function */
-    this.subscriptions.push(
-      this.newViewer$.pipe(
-        switchMapTo(this.sliceRenderEvent$.pipe(
-          takeOnePipe()
-        ))
-      ).subscribe((events) => {
-        for (const idx in [0, 1, 2]) {
-          const ev = events[idx] as CustomEvent
-          this.viewPanels[idx] = ev.target as HTMLElement
-          this.nanometersToOffsetPixelsFn[idx] = ev.detail.nanometersToOffsetPixels
-        }
-      }),
-    )
-
-    this.subscriptions.push(
-      this.newViewer$.pipe(
-        switchMapTo(fromEvent(this.elementRef.nativeElement, 'perpspectiveRenderEvent').pipe(
-          take(1),
-        )),
-      ).subscribe(ev => this.viewPanels[3] = ((ev as CustomEvent).target) as HTMLElement),
-    )
-
-    this.subscriptions.push(
-      this.redrawLayout$.subscribe(([mode, panelOrder]) => {
-        const viewPanels = panelOrder.split('').map(v => Number(v)).map(idx => this.viewPanels[idx]) as [HTMLElement, HTMLElement, HTMLElement, HTMLElement]
-        /**
-         * TODO be smarter with event stream
-         */
-        if (!this.nehubaViewer) { return }
-
-        /**
-         * TODO smarter with event stream
-         */
-        if (!viewPanels.every(v => !!v)) { return }
-
-        switch (mode) {
-        case PANELS.H_ONE_THREE: {
-          const element = this.removeExistingPanels()
-          const newEl = getHorizontalOneThree(viewPanels)
-          element.appendChild(newEl)
-          break;
-        }
-        case PANELS.V_ONE_THREE: {
-          const element = this.removeExistingPanels()
-          const newEl = getVerticalOneThree(viewPanels)
-          element.appendChild(newEl)
-          break;
-        }
-        case PANELS.FOUR_PANEL: {
-          const element = this.removeExistingPanels()
-          const newEl = getFourPanel(viewPanels)
-          element.appendChild(newEl)
-          break;
-        }
-        case PANELS.SINGLE_PANEL: {
-          const element = this.removeExistingPanels()
-          const newEl = getSinglePanel(viewPanels)
-          element.appendChild(newEl)
-          break;
-        }
-        default:
-        }
-        for (const panel of viewPanels) {
-          (panel as HTMLElement).classList.add('neuroglancer-panel')
-        }
-
-        // TODO needed to redraw?
-        // see https://trello.com/c/oJOnlc6v/60-enlarge-panel-allow-user-rearrange-panel-position
-        // further investigaation required
-        this.nehubaViewer.redraw()
-      }),
-    )
-
-    this.subscriptions.push(
-      this.fetchedSpatialDatasets$.subscribe(datasets => {
-        this.landmarksLabelIndexMap = new Map(datasets.map((v, idx) => [idx, v]) as Array<[number, any]>)
-        this.landmarksNameMap = new Map(datasets.map((v, idx) => [v.name, idx] as [string, number]))
-      }),
-    )
-
-    /**
-     * TODO deprecate, but document the method
-     */
-    this.subscriptions.push(
-      combineLatest(
-        this.fetchedSpatialDatasets$,
-      ).subscribe(([fetchedSpatialData]) => {
-        this.fetchedSpatialData = fetchedSpatialData
-
-        if (this.fetchedSpatialData?.length > 0) {
-          this.nehubaViewer.addSpatialSearch3DLandmarks(
-            this.fetchedSpatialData
-              .map(data => data.geometry.type === 'point'
-                ? (data.geometry as IPointLandmarkGeometry).position
-                : data.geometry.type === 'plane'
-                  ? [
-                    (data.geometry as IPlaneLandmarkGeometry).corners,
-                    [[0, 1, 2], [0, 2, 3]],
-                  ]
-                  : data.geometry.type === 'mesh'
-                    ? [
-                      (data.geometry as IOtherLandmarkGeometry).vertices,
-                      (data.geometry as IOtherLandmarkGeometry).meshIdx,
-                    ]
-                    : null),
-          )
-        } else {
-          if (this.nehubaViewer && this.nehubaViewer.removeSpatialSearch3DLandmarks instanceof Function) {
-            this.nehubaViewer.removeSpatialSearch3DLandmarks()
-          }
-        }
-      }),
-    )
-
-    this.subscriptions.push(
-      this.userLandmarks$.pipe(
-        withLatestFrom(
-          this.nehubaViewerPerspectiveOctantRemoval$
-        )
-      ).subscribe(([landmarks, flag]) => {
-        if (this.nehubaContainerDirective) {
-          this.nehubaContainerDirective.toggleOctantRemoval(
-            landmarks.length > 0 ? false : flag
-          )
-        }
-        if (this.nehubaViewer) {
-          this.nehubaViewer.updateUserLandmarks(landmarks)
-        }
-      }),
-    )
-
-    this.subscriptions.push(
-      this.newViewer$.pipe(
-        skip(1),
-      ).subscribe(() => {
-
-        /* on selecting of new template, remove additional nglayers */
-        const baseLayerNames = Object.keys(this.selectedTemplate.nehubaConfig.dataset.initialNgState.layers)
-        this.ngLayersRegister.layers
-          .filter(layer => baseLayerNames?.findIndex(l => l === layer.name) < 0)
-          .map(l => l.name)
-          .forEach(layerName => {
-            this.store.dispatch(ngViewerActionRemoveNgLayer({
-              layer: {
-                name: layerName
-              }
-            }))
-          })
-      }),
-    )
-
-    this.subscriptions.push(
-      this.templateSelected$.subscribe(() => this.destroynehuba()),
-    )
-
-    /* order of subscription will determine the order of execution */
-    this.subscriptions.push(
-      this.newViewer$.pipe(
-        map(templateSelected => {
-          const deepCopiedState = JSON.parse(JSON.stringify(templateSelected))
-          const navigation = deepCopiedState.nehubaConfig.dataset.initialNgState.navigation
-          if (!navigation) {
-            return deepCopiedState
-          }
-          navigation.zoomFactor = calculateSliceZoomFactor(navigation.zoomFactor)
-          deepCopiedState.nehubaConfig.dataset.initialNgState.navigation = navigation
-          return deepCopiedState
-        }),
-        withLatestFrom(
-          this.selectedParcellation$.pipe(
-            startWith(null),
-          )
-        ),
-      ).subscribe(([templateSelected, parcellationSelected]) => {
-
-        this.selectedTemplate = templateSelected
-        this.createNewNehuba(templateSelected)
-        const foundParcellation = parcellationSelected
-          && templateSelected?.parcellations?.find(parcellation => parcellationSelected.name === parcellation.name)
-        this.handleParcellation(foundParcellation || templateSelected.parcellations[0])
-
-        const nehubaConfig = templateSelected.nehubaConfig
-        const initialSpec = nehubaConfig.dataset.initialNgState
-        const {layers} = initialSpec
-
-        const dispatchLayers = Object.keys(layers).map(key => {
-          const layer = {
-            name : key,
-            source : layers[key].source,
-            mixability : layers[key].type === 'image'
-              ? 'base'
-              : 'mixable',
-            visible : typeof layers[key].visible === 'undefined'
-              ? true
-              : layers[key].visible,
-            transform : typeof layers[key].transform === 'undefined'
-              ? null
-              : layers[key].transform,
-          }
-          this.ngLayersRegister.layers.push(layer)
-          return layer
-        })
-
-        this.store.dispatch(ngViewerActionAddNgLayer({
-          layer: dispatchLayers
-        }))
-      })
-    )
-
-    let prevParcellation = null
-
-    this.subscriptions.push(
-
-      combineLatest([
-        this.selectedRegions$.pipe(
-          distinctUntilChanged(),
-        ),
-        this.hideSegmentations$.pipe(
-          distinctUntilChanged(),
-        ),
-        this.ngLayers$.pipe(
-          map(state => state.forceShowSegment),
-          distinctUntilChanged(),
-        ),
-        this.selectedParcellation$,
-        this.store.pipe(
-          select('viewerState'),
-          select('overwrittenColorMap'),
-          distinctUntilChanged()
-        )
-      ]).pipe(
-        delayWhen(() => timer())
-      ).subscribe(([regions, hideSegmentFlag, forceShowSegment, selectedParcellation, overwrittenColorMap]) => {
-        if (!this.nehubaViewer) { return }
-
-        const { ngId: defaultNgId } = selectedParcellation
-
-        /* selectedregionindexset needs to be updated regardless of forceshowsegment */
-        this.selectedRegionIndexSet = !prevParcellation || prevParcellation === selectedParcellation?
-          new Set(regions.map(({ngId = defaultNgId, labelIndex}) => generateLabelIndexId({ ngId, labelIndex }))) : new Set()
-
-        if ( forceShowSegment === false || (forceShowSegment === null && hideSegmentFlag) ) {
-          this.nehubaViewer.hideAllSeg()
-          return
-        }
-
-        this.selectedRegionIndexSet.size > 0 && !overwrittenColorMap
-          ? this.nehubaViewer.showSegs([...this.selectedRegionIndexSet])
-          : this.nehubaViewer.showAllSeg()
-
-        prevParcellation = selectedParcellation
-      }),
-    )
-
-    this.subscriptions.push(
-      this.ngLayers$.subscribe(ngLayersInterface => {
-        if (!this.nehubaViewer) { return }
-
-        const newLayers = ngLayersInterface.layers.filter(l => this.ngLayersRegister.layers?.findIndex(ol => ol.name === l.name) < 0)
-        const removeLayers = this.ngLayersRegister.layers.filter(l => ngLayersInterface.layers?.findIndex(nl => nl.name === l.name) < 0)
-
-        if (newLayers?.length > 0) {
-          const newLayersObj: any = {}
-          newLayers.forEach(({ name, source, ...rest }) => newLayersObj[name] = {
-            ...rest,
-            source,
-            // source: getProxyUrl(source),
-            // ...getProxyOther({source})
-          })
-
-          if (!this.nehubaViewer.nehubaViewer || !this.nehubaViewer.nehubaViewer.ngviewer) {
-            this.nehubaViewer.initNiftiLayers.push(newLayersObj)
-          } else {
-            this.nehubaViewer.loadLayer(newLayersObj)
-          }
-          this.ngLayersRegister.layers = this.ngLayersRegister.layers.concat(newLayers)
-        }
-
-        if (removeLayers?.length > 0) {
-          removeLayers.forEach(l => {
-            if (this.nehubaViewer.removeLayer({
-              name : l.name,
-            })) {
-              this.ngLayersRegister.layers = this.ngLayersRegister.layers.filter(rl => rl.name !== l.name)
-            }
-          })
-        }
-      }),
-    )
-
-    this.subscriptions.push(
-      this.selectedParcellation$.subscribe(this.handleParcellation.bind(this))
-    )
-
-    /* setup init view state */
-
-    this.subscriptions.push(
-      this.selectedRegions$.pipe(
-        filter(() => !!this.nehubaViewer),
-      ).subscribe(regions => {
-        this.nehubaViewer.initRegions = regions.map(({ ngId, labelIndex }) => generateLabelIndexId({ ngId, labelIndex }))
-      })
-    )
-
-    this.subscriptions.push(this.selectedRegions$.subscribe(sr => {
-      this.selectedRegions = sr
-    }))
-
-    /** switch side nav */
-    this.subscriptions.push(
-      this.alwaysHideMinorPanel$.pipe(
-        distinctUntilChanged()
-      ).subscribe(flag => {
-        if (!flag) {
-          this.matDrawerMinor && this.matDrawerMinor.open()
-          this.navSideDrawerMainSwitch && this.navSideDrawerMainSwitch.open()
-        }
-      })
-    )
-
-    this.subscriptions.push(
-      this.selectedRegions$.subscribe(regions => {
-        this.selectedRegions = regions
-      })
-    )
-
-    /* handler to open/select landmark */
-    const clickObs$ = fromEvent(this.elementRef.nativeElement, 'click')
-
-    this.subscriptions.push(
-      clickObs$.pipe(
-        buffer(
-          clickObs$.pipe(
-            debounceTime(200),
-          ),
-        ),
-        filter(arr => arr?.length >= 2),
-      )
-        .subscribe(() => {
-          const { currentOnHover } = this
-          this.store.dispatch(viewerStateDblClickOnViewer({
-            payload: { ...currentOnHover }
-          }))
-        }),
-    )
-
-    this.subscriptions.push(
-      this.selectedLandmarks$.pipe(
-        map(lms => lms.map(lm => this.landmarksNameMap.get(lm.name))),
-        debounceTime(16),
-      ).subscribe(indices => {
-        const filteredIndices = indices.filter(v => typeof v !== 'undefined' && v !== null)
-        if (this.nehubaViewer) {
-          this.nehubaViewer.spatialLandmarkSelectionChanged(filteredIndices)
-        }
-      }),
-    )
-  }
-
-  // datasetViewerRegistry : Set<string> = new Set()
-  public showObliqueScreen$: Observable<boolean>
-  public showObliqueSelection$: Observable<boolean>
-  public showObliqueRotate$: Observable<boolean>
-
-  private currOnHoverObsSub: Subscription
-  public ngOnChanges() {
-    this.currOnHoverObsSub && this.currOnHoverObsSub.unsubscribe()
-    if (this.currentOnHoverObs$) {
-      this.currOnHoverObsSub = this.currentOnHoverObs$.subscribe(({ segments }) => this.onHoverSegments$.next(segments))
-    }
-  }
-
-  public ngOnDestroy() {
-    this.subscriptions.forEach(s => s.unsubscribe())
-  }
-
-  public toggleMaximiseMinimise(index: number) {
-    this.store.dispatch(ngViewerActionToggleMax({
-      payload: { index }
-    }))
-  }
-
-  public tunableMobileProperties: ITunableProp[] = []
-
-
-  public selectedProp = null
-
-  public returnTruePos(quadrant: number, data: any) {
-    const pos = quadrant > 2
-      ? [0, 0, 0]
-      : this.nanometersToOffsetPixelsFn && this.nanometersToOffsetPixelsFn[quadrant]
-        ? this.nanometersToOffsetPixelsFn[quadrant](data.geometry.position.map(n => n * 1e6))
-        : [0, 0, 0]
-    return pos
-  }
-
-  public getPositionX(quadrant: number, data: any) {
-    return this.returnTruePos(quadrant, data)[0]
-  }
-  public getPositionY(quadrant: number, data: any) {
-    return this.returnTruePos(quadrant, data)[1]
-  }
-  public getPositionZ(quadrant: number, data: any) {
-    return this.returnTruePos(quadrant, data)[2]
-  }
-
-  public handleMouseEnterCustomLandmark(lm) {
-    this.store.dispatch(
-      viewerStateMouseOverCustomLandmark({
-        payload: { userLandmark: lm }
-      })
-    )
-  }
-
-  public handleMouseLeaveCustomLandmark(lm) {
-    this.store.dispatch(
-      viewerStateMouseOverCustomLandmark({
-        payload: { userLandmark: null }
-      })
-    )
-  }
-
-  // handles mouse enter/leave landmarks in 2D
-  public handleMouseEnterLandmark(spatialData: any) {
-    spatialData.highlight = true
-    this.store.dispatch({
-      type : MOUSE_OVER_LANDMARK,
-      landmark : spatialData._label,
-    })
-  }
-
-  public handleMouseLeaveLandmark(spatialData: any) {
-    spatialData.highlight = false
-    this.store.dispatch({
-      type : MOUSE_OVER_LANDMARK,
-      landmark : null,
-    })
-  }
-
-  private handleParcellation(parcellation: any) {
-    /**
-     * parcellaiton may be undefined
-     */
-    if ( !(parcellation && parcellation.regions)) {
-      return
-    }
-
-    /**
-     * first, get all all the ngIds, including parent id from parcellation (if defined)
-     */
-    const ngIds = getNgIds(parcellation.regions).concat( parcellation.ngId ? parcellation.ngId : [])
-
-    this.multiNgIdsRegionsLabelIndexMap = getMultiNgIdsRegionsLabelIndexMap(parcellation)
-
-    this.nehubaViewer.multiNgIdsLabelIndexMap = this.multiNgIdsRegionsLabelIndexMap
-    this.nehubaViewer.auxilaryMeshIndices = parcellation.auxillaryMeshIndices || []
-
-    /* TODO replace with proper KG id */
-    /**
-     * need to set unique array of ngIds, or else workers will be overworked
-     */
-    this.nehubaViewer.ngIds = Array.from(new Set(ngIds))
-    this.selectedParcellation = parcellation
-  }
-
-  /* related spatial search */
-  public spatialSearchPagination: number = 0
-
-  private destroynehuba() {
-    /**
-     * TODO if plugin subscribes to viewerHandle, and then new template is selected, changes willl not be be sent
-     * could be considered as a bug.
-     */
-    this.setViewerHandle && this.setViewerHandle(null)
-    this.nehubaContainerDirective.clear()
-
-    this.nehubaViewer = null
-
-    this.cdr.detectChanges()
-  }
-
-  private createNewNehuba(template: any) {
-
-    this.nehubaContainerDirective.createNehubaInstance(template)
-    this.nehubaViewer = this.nehubaContainerDirective.nehubaViewerInstance
-
-    this.setupViewerHandleApi()
-  }
-
-  private setupViewerHandleApi() {
-    const viewerHandle = {
-      setNavigationLoc : (coord, realSpace?) => this.nehubaViewer.setNavigationState({
-        position : coord,
-        positionReal : typeof realSpace !== 'undefined' ? realSpace : true,
-      }),
-      /* TODO introduce animation */
-      moveToNavigationLoc : (coord, realSpace?) => {
-        this.store.dispatch({
-          type: CHANGE_NAVIGATION,
-          navigation: {
-            position: coord,
-            animation: {},
-          },
-        })
-      },
-      setNavigationOri : (quat) => this.nehubaViewer.setNavigationState({
-        orientation : quat,
-      }),
-      /* TODO introduce animation */
-      moveToNavigationOri : (quat) => this.nehubaViewer.setNavigationState({
-        orientation : quat,
-      }),
-      showSegment : (_labelIndex) => {
-        /**
-         * TODO reenable with updated select_regions api
-         */
-        this.log.warn(`showSegment is temporarily disabled`)
-
-        // if(!this.selectedRegionIndexSet.has(labelIndex))
-        //   this.store.dispatch({
-        //     type : SELECT_REGIONS,
-        //     selectRegions :  [labelIndex, ...this.selectedRegionIndexSet]
-        //   })
-      },
-      add3DLandmarks : landmarks => {
-        // TODO check uniqueness of ID
-        if (!landmarks.every(l => isDefined(l.id))) {
-          throw new Error('every landmarks needs to be identified with the id field')
-        }
-        if (!landmarks.every(l => isDefined(l.position))) {
-          throw new Error('every landmarks needs to have position defined')
-        }
-        if (!landmarks.every(l => l.position.constructor === Array) || !landmarks.every(l => l.position.every(v => !isNaN(v))) || !landmarks.every(l => l.position.length == 3)) {
-          throw new Error('position needs to be a length 3 tuple of numbers ')
-        }
-
-        this.store.dispatch(viewerStateAddUserLandmarks({
-          landmarks
-        }))
-      },
-      remove3DLandmarks : landmarkIds => {
-        this.store.dispatch(viewreStateRemoveUserLandmarks({
-          payload: { landmarkIds }
-        }))
-      },
-      hideSegment : (_labelIndex) => {
-        /**
-         * TODO reenable with updated select_regions api
-         */
-        this.log.warn(`hideSegment is temporarily disabled`)
-
-        // if(this.selectedRegionIndexSet.has(labelIndex)){
-        //   this.store.dispatch({
-        //     type :SELECT_REGIONS,
-        //     selectRegions : [...this.selectedRegionIndexSet].filter(num=>num!==labelIndex)
-        //   })
-        // }
-      },
-      showAllSegments : () => {
-        const selectRegionIds = []
-        this.multiNgIdsRegionsLabelIndexMap.forEach((map, ngId) => {
-          Array.from(map.keys()).forEach(labelIndex => {
-            selectRegionIds.push(generateLabelIndexId({ ngId, labelIndex }))
-          })
-        })
-        this.store.dispatch(viewerStateSelectRegionWithIdDeprecated({
-          selectRegionIds
-        }))
-      },
-      hideAllSegments : () => {
-        this.store.dispatch(viewerStateSelectRegionWithIdDeprecated({
-          selectRegionIds: []
-        }))
-      },
-      segmentColourMap : new Map(),
-      getLayersSegmentColourMap: () => {
-        const newMainMap = new Map()
-        for (const [key, colormap] of this.nehubaViewer.multiNgIdColorMap.entries()) {
-          const newColormap = new Map()
-          newMainMap.set(key, newColormap)
-
-          for (const [lableIndex, entry] of colormap.entries()) {
-            newColormap.set(lableIndex, JSON.parse(JSON.stringify(entry)))
-          }
-        }
-        return newMainMap
-      },
-      applyColourMap : (_map) => {
-        throw new Error(`apply color map has been deprecated. use applyLayersColourMap instead`)
-      },
-      applyLayersColourMap: (map) => {
-        this.nehubaViewer.setColorMap(map)
-      },
-      loadLayer : (layerObj) => this.nehubaViewer.loadLayer(layerObj),
-      removeLayer : (condition) => this.nehubaViewer.removeLayer(condition),
-      setLayerVisibility : (condition, visible) => this.nehubaViewer.setLayerVisibility(condition, visible),
-      mouseEvent : merge(
-        fromEvent(this.elementRef.nativeElement, 'click').pipe(
-          map((ev: MouseEvent) => ({eventName : 'click', event: ev})),
-        ),
-        fromEvent(this.elementRef.nativeElement, 'mousemove').pipe(
-          map((ev: MouseEvent) => ({eventName : 'mousemove', event: ev})),
-        ),
-        /**
-         * neuroglancer prevents propagation, so use capture instead
-         */
-        Observable.create(observer => {
-          this.elementRef.nativeElement.addEventListener('mousedown', event => observer.next({eventName: 'mousedown', event}), true)
-        }) as Observable<{eventName: string, event: MouseEvent}>,
-        fromEvent(this.elementRef.nativeElement, 'mouseup').pipe(
-          map((ev: MouseEvent) => ({eventName : 'mouseup', event: ev})),
-        ),
-      ) ,
-      mouseOverNehuba : this.onHoverSegment$.pipe(
-        tap(() => console.warn('mouseOverNehuba observable is becoming deprecated. use mouseOverNehubaLayers instead.')),
-      ),
-      mouseOverNehubaLayers: this.onHoverSegments$,
-      mouseOverNehubaUI: this.currentOnHoverObs$.pipe(
-        map(({ landmark, segments, userLandmark: customLandmark }) => ({ segments, landmark, customLandmark })),
-        shareReplay(1),
-      ),
-      getNgHash : this.nehubaViewer.getNgHash,
-    }
-
-    this.setViewerHandle && this.setViewerHandle(viewerHandle)
-  }
-
-  public setOctantRemoval(octantRemovalFlag: boolean) {
-    this.store.dispatch(
-      ngViewerActionSetPerspOctantRemoval({
-        octantRemovalFlag
-      })
-    )
-  }
-
-  public zoomNgView(panelIndex: number, factor: number) {
-    const ngviewer = this.nehubaViewer?.nehubaViewer?.ngviewer
-    if (!ngviewer) throw new Error(`ngviewer not defined!`)
-
-    /**
-     * panelIndex < 3 === slice view
-     */
-    if (panelIndex < 3) {
-      /**
-       * factor > 1 === zoom out
-       */
-      ngviewer.navigationState.zoomBy(factor)
-    } else {
-      ngviewer.perspectiveNavigationState.zoomBy(factor)
-    }
-  }
-
-  public clearPreviewingDataset(id: string){
-    /**
-     * clear all preview
-     */
-    this.store.dispatch(
-      id
-        ? uiActionHideDatasetWithId({ id })
-        : uiActionHideAllDatasets()
-    )
-  }
-}
diff --git a/src/ui/nehubaContainer/nehubaContainer.style.css b/src/ui/nehubaContainer/nehubaContainer.style.css
deleted file mode 100644
index 956ab12378101e5faacc1e606db186e7a95c68e3..0000000000000000000000000000000000000000
--- a/src/ui/nehubaContainer/nehubaContainer.style.css
+++ /dev/null
@@ -1,182 +0,0 @@
-:host
-{
-  width:100%;
-  height:100%;
-  position:relative;
-}
-
-atlas-layer-container
-{
-  margin-bottom: 1em;
-  margin-left: 1em;
-  width: 30em;
-  height: 50em;
-}
-
-current-layout
-{
-  top: 0;
-  left: 0;
-  /** z index of current layout has to be higher than that of layout-floating-container, or status container will cover panel control */
-  /** TODO decide which behaviour is more nature */
-  z-index: 6;
-}
-
-div.loadingIndicator
-{
-  left: auto;
-  top: auto;
-  right: 0;
-  bottom: 0;
-  margin-right: 0.2em;
-  margin-bottom: 0.2em;
-  width: 100%;
-  position:absolute;
-  height:2em;
-  display: flex;
-  flex-direction: row-reverse;
-}
-
-div.loadingIndicator >>> div.spinnerAnimationCircle
-{
-  font-size:150%;
-}
-
-[perspectiveLoadingText]
-{
-  margin-right: 1em;
-}
-
-:host-context([darktheme="true"]) [perspectiveLoadingText]
-{
-  color:rgba(255,255,255,0.8);
-}
-
-div#scratch-pad
-{
-  position: absolute;
-  top: 0;
-  left: 0;
-  width: 100%;
-  height: 100%;
-  pointer-events: none;
-}
-
-.overlay-btn-container
-{
-  position: absolute;
-  bottom: 0;
-  right: 0;
-}
-
-/* if not mobile, then show on hover */
-
-.opacity-crossfade
-{
-  transition: opacity 170ms ease-in-out,
-    transform 250ms ease-in-out;
-}
-
-.opacity-crossfade
-{
-
-  opacity: 0.0;
-  pointer-events: none;
-}
-
-.opacity-crossfade.onHover,
-.opacity-crossfade:hover,
-:host-context([ismobile="true"]) .opacity-crossfade.always-show-touchdevice
-{
-  opacity: 1.0 !important;
-  pointer-events: all !important;
-}
-
-.screen-overlay
-{
-  background-color: rgba(255, 255, 255, 0.7);
-}
-
-:host-context([darktheme="true"]) .screen-overlay
-{
-  background-color: rgba(0, 0, 0, 0.7);
-}
-
-.feature-card
-{
-
-  height: 20em;
-  max-height: 20em;
-  flex: 0 0 20em;
-  align-items: flex-start;
-}
-
-.region-populated .feature-card
-{
-  height: 25em;
-  max-height: 25em;
-  flex: 0 0 25em;
-  align-items: flex-start;
-}
-
-.region-text-search-autocomplete-position
-{
-  z-index: 10;
-  position: sticky;
-  top: 0.5rem;
-}
-
-.collapse-position
-{
-  z-index: 10;
-  position: sticky;
-  bottom: 0.5rem;
-}
-
-.placeholder-region-detail
-{
-  padding: 6rem 1rem 1rem 1rem;
-}
-
-.explore-btn
-{
-  margin-top: 4.5rem;
-  width: 100%;
-}
-
-.modality-card-container
-{
-  overflow-x: scroll;
-}
-
-.modality-card
-{
-  width: 10em;
-}
-
-.spacer
-{
-  height: 6rem;
-}
-
-.content-transclusion-top-left
-{
-  margin-left: 3.5rem;
-}
-
-.tab-toggle
-{
-  margin-left: -2rem;
-  padding-right: 0.75rem;
-  margin-right: 1rem;
-  text-align: right;
-}
-
-.accordion-icon
-{
-  width:1.5rem;
-}
-.tab-toggle-container
-{
-  margin-top: 1.5rem;
-}
diff --git a/src/ui/nehubaContainer/nehubaContainer.template.html b/src/ui/nehubaContainer/nehubaContainer.template.html
deleted file mode 100644
index 1ce4f539812f6d98d113b561380f8fd5a34cccc6..0000000000000000000000000000000000000000
--- a/src/ui/nehubaContainer/nehubaContainer.template.html
+++ /dev/null
@@ -1,857 +0,0 @@
-<div class="position-absolute h-100 w-100"
-  (touchmove)="$event.preventDefault()"
-  iav-viewer-touch-interface
-  [iav-viewer-touch-interface-v-panels]="viewPanels"
-  [iav-viewer-touch-interface-vp-to-data]="iavContainer?.viewportToDatas"
-  [iav-viewer-touch-interface-ngviewer]="nehubaViewer?.nehubaViewer?.ngviewer"
-  [iav-viewer-touch-interface-nehuba-config]="selectedTemplate?.nehubaConfig">
-  <div
-    iav-nehuba-viewer-container
-    (iavNehubaViewerContainerViewerLoading)="handleViewerLoadedEvent($event)"
-    #iavContainer="iavNehubaViewerContainer">
-  </div>
-</div>
-
-<ui-splashscreen iav-stop="mousedown mouseup touchstart touchmove touchend" *ngIf="!viewerLoaded">
-</ui-splashscreen>
-
-<!-- spatial landmarks overlay -->
-<!-- loading indicator -->
-
-<current-layout *ngIf="viewerLoaded" class="position-absolute w-100 h-100 d-block pe-none">
-  <div class="w-100 h-100 position-relative" cell-i>
-    <ng-content *ngTemplateOutlet="ngPanelOverlayTmpl; context: { panelIndex: panelOrder$ | async | getNthElement : 0 | parseAsNumber }"></ng-content>
-  </div>
-  <div class="w-100 h-100 position-relative" cell-ii>
-    <ng-content *ngTemplateOutlet="ngPanelOverlayTmpl; context: { panelIndex: panelOrder$ | async | getNthElement : 1 | parseAsNumber }"></ng-content>
-  </div>
-  <div class="w-100 h-100 position-relative" cell-iii>
-    <ng-content *ngTemplateOutlet="ngPanelOverlayTmpl; context: { panelIndex: panelOrder$ | async | getNthElement : 2 | parseAsNumber }"></ng-content>
-  </div>
-  <div class="w-100 h-100 position-relative" cell-iv>
-    <ng-content *ngTemplateOutlet="ngPanelOverlayTmpl; context: { panelIndex: panelOrder$ | async | getNthElement : 3 | parseAsNumber }"></ng-content>
-  </div>
-</current-layout>
-
-<layout-floating-container
-  class="overflow-hidden w-100 h-100"
-  [zIndex]="10">
-
-  <!-- drawer #1 -->
-  <mat-drawer-container
-    [iav-switch-initstate]="false"
-    iav-switch
-    #sideNavMasterSwitch="iavSwitch"
-    class="mat-drawer-content-overflow-visible w-100 h-100 position-absolute invisible"
-    [hasBackdrop]="false">
-
-    <!-- sidenav-content -->
-    <mat-drawer class="box-shadow-none border-0 pe-none bg-none col-10 col-sm-10 col-md-5 col-lg-4 col-xl-3 col-xxl-2"
-      mode="side"
-      [attr.data-mat-drawer-primary-open]="matDrawerMaster.opened"
-      [opened]="sideNavMasterSwitch.switchState"
-      [autoFocus]="false"
-      (closedStart)="sideNavSwitch.switchState && matDrawerMinor.close()"
-      (openedStart)="sideNavSwitch.switchState && matDrawerMinor.open()"
-      [disableClose]="true"
-      #matDrawerMaster="matDrawer">
-
-      <div class="h-0 w-100 region-text-search-autocomplete-position">
-        <ng-container *ngTemplateOutlet="autocompleteTmpl">
-        </ng-container>
-      </div>
-
-      <button mat-raised-button
-        *ngIf="!(alwaysHideMinorPanel$ | async)"
-        [attr.aria-label]="ARIA_LABEL_EXPAND"
-        (click)="matDrawerMinor.open()"
-        class="explore-btn pe-all"
-        [ngClass]="{
-          'darktheme': iavRegion.rgbDarkmode === true,
-          'lighttheme': iavRegion.rgbDarkmode === false
-        }"
-        [style.backgroundColor]="iavRegion?.rgbString || 'accent'">
-        <span class="text iv-custom-comp">
-          Explore
-        </span>
-
-        <div class="hidden"
-          iav-region
-          [region]="(selectedRegions$ | async) && (selectedRegions$ | async)[0]"
-          #iavRegion="iavRegion">
-        </div>
-
-      </button>
-    </mat-drawer>
-    <mat-drawer-content class="visible position-relative">
-
-      <!-- top left overlay -->
-      <div class="content-transclusion-top-left position-absolute top-0 left-0 w-100 d-inline-block pe-none">
-        <ng-content select="[ui-nehuba-container-overlay-top-left]">
-        </ng-content>
-      </div>
-
-      <!-- top right overlay -->
-      <div class="position-absolute top-0 right-0 w-100 d-inline-block pe-none">
-        <ng-content select="[ui-nehuba-container-overlay-top-right]">
-        </ng-content>
-      </div>
-
-      <div *ngIf="viewerLoaded" class="position-absolute z-index-6 top-0 left-0 pe-none tab-toggle-container">
-        <ng-container *ngTemplateOutlet="tabTmpl; context: {
-          isOpen: sideNavMasterSwitch.switchState,
-          regionSelected: selectedRegions$ | async,
-          iavAdditionallayers: iavAdditionalLayers$ | async
-        }">
-
-        </ng-container>
-      </div>
-
-    </mat-drawer-content>
-  </mat-drawer-container>
-
-  <!-- drawer #2 -->
-  <mat-drawer-container
-    [iav-switch-initstate]="!(alwaysHideMinorPanel$ | async)"
-    iav-switch
-    #sideNavSwitch="iavSwitch"
-    class="mat-drawer-content-overflow-visible w-100 h-100 position-absolute invisible"
-    [hasBackdrop]="false">
-
-    <!-- sidenav-content -->
-    <mat-drawer class="darker-bg iv-custom-comp visible col-10 col-sm-10 col-md-5 col-lg-4 col-xl-3 col-xxl-2 d-flex flex-column pe-all"
-      mode="push"
-      [attr.data-mat-drawer-secondary-open]="matDrawerMinor.opened"
-      [autoFocus]="false"
-      #matDrawerMinor="matDrawer"
-      (openedChange)="$event && sideNavSwitch.open()"
-      [disableClose]="true"
-      [@openClose]="sideNavMasterSwitch.switchState && sideNavSwitch.switchState ? 'open' : 'closed'"
-      (@openClose.done)="$event.toState === 'closed' && matDrawerMinor.close()">
-
-      <div class="position-relative d-flex flex-column h-100">
-
-        <!-- TODO dataset preview will become deprecated in the future.
-        Regional feature/data feature will replace it -->
-
-        <div class="hidden"
-          iav-shown-dataset
-          #iavShownDataset="iavShownDataset">
-        </div>
-      
-        <div class="hidden"
-          iav-shown-previews
-          (emitter)="iavAdditionalLayers$.next($event)"
-          #previews="iavShownPreviews">
-        </div>
-
-        <!-- sidenav datasets -->
-        <ng-container *ngIf="iavShownDataset.shownDatasetId$ | async as shownDatasetId">
-          <ng-template [ngIf]="shownDatasetId.length > 0" [ngIfElse]="sideNavVolumePreview">
-            
-            <!-- single dataset side nav panel -->
-            <single-dataset-sidenav-view *ngFor="let id of shownDatasetId"
-              (clear)="clearPreviewingDataset(id)"
-              [fullId]="id"
-              class="bs-border-box ml-15px-n mr-15px-n">
-              <mat-chip *ngIf="regionOfInterest$ && regionOfInterest$ | async as region"
-                region-of-interest
-                iav-region
-                [region]="region"
-                [ngClass]="{
-                  'darktheme':regionDirective.rgbDarkmode === true,
-                  'lighttheme': regionDirective.rgbDarkmode === false
-                }"
-                [style.backgroundColor]="regionDirective.rgbString"
-                #regionDirective="iavRegion">
-                <span class="iv-custom-comp text text-truncate d-inline">
-                  {{ region.name }}
-                </span>
-              </mat-chip>
-            </single-dataset-sidenav-view>
-          </ng-template>
-        </ng-container>
-
-        <!-- preview volumes -->
-        <ng-template #sideNavVolumePreview>
-          <ng-container *ngIf="previews.iavAdditionalLayers$ | async | filterPreviewByType : [previews.FILETYPES.VOLUMES] as volumePreviews">
-            <ng-template [ngIf]="volumePreviews.length > 0" [ngIfElse]="sidenavRegionTmpl">
-              <ng-container *ngFor="let vPreview of volumePreviews">
-                <ng-container *ngTemplateOutlet="sidenavDsPreviewTmpl; context: vPreview">
-  
-                </ng-container>
-              </ng-container>
-            </ng-template>
-          </ng-container>
-        </ng-template>
-
-      </div>
-    </mat-drawer>
-
-    <!-- main-content -->
-    <mat-drawer-content class="visible position-relative">
-
-      <!-- bottom left overlay -->
-      <div class="position-absolute bottom-0 left-0 w-100 d-inline-block pe-none">
-        <ng-content select="[ui-nehuba-container-overlay-bottom-left]">
-        </ng-content>
-      </div>
-
-    </mat-drawer-content>
-  </mat-drawer-container>
-
-</layout-floating-container>
-
-<!-- collapse btn -->
-<ng-template #collapseBtn>
-
-  <div class="h-0 w-100 collapse-position d-flex flex-column justify-content-end align-items-center">
-    <button mat-raised-button class="mat-elevation-z8"
-      [attr.aria-label]="ARIA_LABEL_COLLAPSE"
-      (click)="sideNavSwitch.close()"
-      color="basic">
-      <i class="fas fa-chevron-up"></i>
-      <span>
-        collapse
-      </span>
-    </button>
-  </div>
-</ng-template>
-
-<!-- region sidenav tmpl -->
-<ng-template #sidenavRegionTmpl>
-
-  <!-- region search autocomplete  -->
-
-  <div class="h-0 w-100 region-text-search-autocomplete-position"
-    [@openCloseAnchor]="sideNavSwitch.switchState ? 'open' : 'closed'">
-    <ng-container *ngTemplateOutlet="autocompleteTmpl">
-    </ng-container>
-  </div>
-
-  <div class="flex-shrink-1 flex-grow-1 d-flex flex-column"
-    [ngClass]="{'region-populated': (selectedRegions$ | async).length > 0 }">
-    <!-- region detail -->
-    <ng-container *ngIf="selectedRegions$ | async as selectedRegions; else selectRegionErrorTmpl">
-
-      <!-- single-region-wrapper -->
-      <ng-template [ngIf]="selectedRegions.length === 1" [ngIfElse]="multiRegionWrapperTmpl">
-        <!-- a series of bugs result in requiring this hacky -->
-        <!-- see https://github.com/HumanBrainProject/interactive-viewer/issues/698 -->
-        <ng-container *ngFor="let region of selectedRegions">
-          <ng-container *ngTemplateOutlet="singleRegionTmpl; context: { region: region }">
-          </ng-container>
-        </ng-container>
-      </ng-template>
-      
-      <!-- multi region wrapper -->
-      <ng-template #multiRegionWrapperTmpl>
-        <ng-container *ngTemplateOutlet="multiRegionTmpl; context: {
-          regions: selectedRegions
-        }">
-        </ng-container>
-        <!-- This is a wrapper for multiregion consisting of {{ selectedRegions.length }} regions -->
-      </ng-template>
-
-      <!-- place holder if length === 0 -->
-      <ng-container *ngIf="selectedRegions.length === 0">
-        <ng-container *ngTemplateOutlet="singleRegionTmpl; context: { region: false }">
-        </ng-container>
-      </ng-container>
-    </ng-container>
-
-    <div class="spacer">
-    </div>
-  </div>
-
-  <!-- collapse btn -->
-  <ng-container *ngTemplateOutlet="collapseBtn">
-  </ng-container>
-</ng-template>
-
-<ng-template #sidenavDsPreviewTmpl let-file="file" let-filename="filename" let-datasetId="datasetId">
-  <div class="w-100 flex-grow-1 d-flex flex-column">
-
-    <preview-card class="d-block bs-border-box ml-15px-n mr-15px-n flex-grow-1"
-      [attr.aria-label]="ARIA_LABEL_ADDITIONAL_VOLUME_CONTROL"
-      [datasetId]="datasetId"
-      [filename]="filename">
-    </preview-card>
-
-    <!-- collapse btn -->
-    <ng-container *ngTemplateOutlet="collapseBtn">
-    </ng-container>
-  </div>
-</ng-template>
-
-<ng-template #empty>
-</ng-template>
-
-<ng-template #autocompleteTmpl>
-  <div class="iv-custom-comp bg card w-100 mat-elevation-z8 pe-all">
-    <region-text-search-autocomplete class="w-100 pt-2 flex-shrink-0 flex-grow-0">
-    </region-text-search-autocomplete>
-  </div>
-</ng-template>
-
-<ng-template #selectRegionErrorTmpl>
-  SELECT REGION ERROR
-</ng-template>
-
-<!-- single region template -->
-<div id="scratch-pad">
-</div>
-
-<!-- mobile nub. may be required when more advanced control is required on mobile. for now, disabled -->
-<mobile-overlay
-  *ngIf="false && (useMobileUI$ | async) && viewerLoaded"
-  [iav-mobile-overlay-guide-tmpl]="mobileOverlayGuide"
-  [tunableProperties]="tunableMobileProperties"
-  [iav-mobile-overlay-hide-ctrl-btn]="(panelMode$ | async) !== 'SINGLE_PANEL'"
-  [iav-mobile-overlay-ctrl-btn-pos]="panelMode$ | async | mobileControlNubStylePipe">
-
-</mobile-overlay>
-
-<ng-template #mobileOverlayGuide>
-  <div>
-    <i class="fas fa-arrows-alt-v"></i>
-    <span>
-      Select item
-    </span>
-  </div>
-  <div>
-    <i class="fas fa-arrows-alt-h"></i>
-    <span>
-      Modify item value
-    </span>
-  </div>
-</ng-template>
-
-<!-- region tmpl placeholder -->
-<ng-template #regionPlaceholderTmpl>
-  <div class="placeholder-region-detail bs-border-box ml-15px-n mr-15px-n mat-elevation-z4">
-    <span class="text-muted">
-      Select a region by clicking on the viewer or search from above
-    </span>
-  </div>
-</ng-template>
-
-<!-- expansion tmpl -->
-<ng-template #ngMatAccordionTmpl
-  let-title="title"
-  let-desc="desc"
-  let-iconClass="iconClass"
-  let-iconTooltip="iconTooltip"
-  let-iavNgIf="iavNgIf"
-  let-content="content">
-  <mat-expansion-panel
-    [attr.data-opened]="expansionPanel.expanded"
-    [attr.data-mat-expansion-title]="title"
-    hideToggle
-    *ngIf="iavNgIf"
-    #expansionPanel="matExpansionPanel">
-
-    <mat-expansion-panel-header>
-
-      <!-- title -->
-      <mat-panel-title>
-        {{ title }}
-      </mat-panel-title>
-
-      <!-- desc + icon -->
-      <mat-panel-description class="d-flex align-items-center justify-content-end"
-        [matTooltip]="iconTooltip">
-        <span class="mr-3">{{ desc }}</span>
-        <span class="accordion-icon d-inline-flex justify-content-center">
-          <i [class]="iconClass"></i>
-        </span>
-      </mat-panel-description>
-
-    </mat-expansion-panel-header>
-
-    <!-- content -->
-    <ng-container *ngTemplateOutlet="content; context: { expansionPanel: expansionPanel }">
-    </ng-container>
-  </mat-expansion-panel>
-</ng-template>
-
-
-<!-- multi region tmpl -->
-<ng-template #multiRegionTmpl let-regions="regions">
-  <ng-template [ngIf]="regions.length > 0" [ngIfElse]="regionPlaceholderTmpl">
-    <region-menu
-      [showRegionInOtherTmpl]="false"
-      [region]="{
-        name: CONST.MULTI_REGION_SELECTION
-      }"
-      class="bs-border-box ml-15px-n mr-15px-n mat-elevation-z4">
-    </region-menu>
-
-    <!-- other regions detail accordion -->
-    <mat-accordion class="bs-border-box ml-15px-n mr-15px-n mt-2">
-
-      <!--  regional features-->
-      <ng-template #regionalFeaturesTmpl>
-        <data-browser
-          [parcellation]="selectedParcellation"
-          [disableVirtualScroll]="true"
-          [regions]="regions">
-        </data-browser>
-      </ng-template>
-
-      <div class="hidden"
-        iav-databrowser-directive
-        [regions]="regions"
-        #iavDbDirective="iavDatabrowserDirective">
-      </div>
-  
-      <ng-container *ngTemplateOutlet="ngMatAccordionTmpl; context: {
-        title: CONST.REGIONAL_FEATURES,
-        desc: iavDbDirective?.dataentries?.length,
-        iconClass: 'fas fa-database',
-        iconTooltip: iavDbDirective?.dataentries?.length | regionAccordionTooltipTextPipe : 'regionalFeatures',
-        iavNgIf: iavDbDirective?.dataentries?.length,
-        content: regionalFeaturesTmpl
-      }">
-      </ng-container>
-
-      <!-- Multi regions include -->
-      <ng-template #multiRegionInclTmpl>
-        <mat-chip-list>
-          <mat-chip *ngFor="let r of regions"
-            iav-region
-            [region]="r"
-            [ngClass]="{
-              'darktheme':regionDirective.rgbDarkmode === true,
-              'lighttheme': regionDirective.rgbDarkmode === false
-            }"
-            [style.backgroundColor]="regionDirective.rgbString"
-            #regionDirective="iavRegion">
-            <span class="iv-custom-comp text text-truncate d-inline pl-4">
-              {{ r.name }}
-            </span>
-          </mat-chip>
-        </mat-chip-list>
-      </ng-template>
-      
-      <ng-container *ngTemplateOutlet="ngMatAccordionTmpl; context: {
-        title: 'Brain regions',
-        desc: regions.length,
-        iconClass: 'fas fa-brain',
-        iavNgIf: true,
-        content: multiRegionInclTmpl
-      }">
-      </ng-container>
-
-    </mat-accordion>
-  </ng-template>
-</ng-template>
-
-
-<!-- single region tmpl -->
-<ng-template #singleRegionTmpl let-region="region">
-  <!-- region detail -->
-  <ng-container *ngIf="region; else regionPlaceholderTmpl">
-    <region-menu
-      [showRegionInOtherTmpl]="false"
-      [region]="region"
-      class="bs-border-box ml-15px-n mr-15px-n mat-elevation-z4">
-    </region-menu>
-  </ng-container>
-
-  <!-- other region detail accordion -->
-  <mat-accordion *ngIf="region"
-    class="bs-border-box ml-15px-n mr-15px-n mt-2"
-    iav-region
-    [region]="region"
-    #iavRegion="iavRegion">
-
-    <!-- desc -->
-    <ng-container *ngFor="let ods of (region.originDatasets || [])">
-      <ng-template #regionDescTmpl>
-        <single-dataset-view
-          [hideTitle]="true"
-          [hideExplore]="true"
-          [hidePreview]="true"
-          [hidePinBtn]="true"
-          [hideDownloadBtn]="true"
-          [kgSchema]="ods.kgSchema"
-          [kgId]="ods.kgId">
-
-        </single-dataset-view>
-      </ng-template>
-      <ng-container *ngTemplateOutlet="ngMatAccordionTmpl; context: {
-        title: 'Description',
-        iconClass: 'fas fa-info',
-        iavNgIf: true,
-        content: regionDescTmpl
-      }">
-
-      </ng-container>
-    </ng-container>
-
-    <!-- Explore in other template -->
-    <ng-container *ngIf="iavRegion.regionInOtherTemplates$ | async as regionInOtherTemplates">
-
-      <ng-template #exploreInOtherTmpl>
-        <mat-card *ngFor="let sameRegion of regionInOtherTemplates"
-          class="p-0 border-0 box-shadow-none mt-1 tb-1 cursor-pointer" 
-          (click)="iavRegion.changeView(sameRegion)"
-          [matTooltip]="sameRegion.template.name + (sameRegion.hemisphere ? (' - ' + sameRegion.hemisphere) : '')"
-          mat-ripple>
-          <small>
-            {{ sameRegion.template.name + (sameRegion.hemisphere ? (' - ' + sameRegion.hemisphere) : '') }}
-          </small>
-        </mat-card>
-      </ng-template>
-
-      <ng-container *ngTemplateOutlet="ngMatAccordionTmpl; context: {
-        title: 'Explore in other templates',
-        desc: regionInOtherTemplates.length,
-        iconClass: 'fas fa-brain',
-        iconTooltip: regionInOtherTemplates.length | regionAccordionTooltipTextPipe : 'regionInOtherTmpl',
-        iavNgIf: regionInOtherTemplates.length,
-        content: exploreInOtherTmpl
-      }">
-
-
-      </ng-container>
-    </ng-container>
-
-    <!--  regional features-->
-    <ng-template #regionalFeaturesTmpl let-expansionPanel="expansionPanel">
-
-      <data-browser
-        *ngIf="expansionPanel.expanded" 
-        [disableVirtualScroll]="true"
-        [regions]="[region]">
-      </data-browser>
-    </ng-template>
-
-    <div class="hidden" iav-databrowser-directive
-      [regions]="[region]"
-      #iavDbDirective="iavDatabrowserDirective">
-    </div>
-
-    <!-- if dataset is loading -->
-    <ng-template [ngIf]="iavDbDirective?.fetchingFlag" [ngIfElse]="featureLoadedTmpl">
-      <div class="d-flex justify-content-center">
-        <spinner-cmp></spinner-cmp>
-      </div>
-    </ng-template>
-
-    <ng-template #featureLoadedTmpl>
-
-      <!-- place holder content, if no regional features or connectivity or change ref space options are available -->
-      <ng-template [ngIf]="iavDbDirective?.dataentries?.length === 0
-        && !(selectedParcellation?.hasAdditionalViewMode?.includes('connectivity'))">
-        <div class="p-4">
-          {{ CONST.NO_ADDIONTAL_INFO_AVAIL }}
-        </div>
-      </ng-template>
-
-    </ng-template>
-
-
-    <ng-container *ngTemplateOutlet="ngMatAccordionTmpl; context: {
-      title: CONST.REGIONAL_FEATURES,
-      desc: iavDbDirective?.dataentries?.length,
-      iconClass: 'fas fa-database',
-      iconTooltip: iavDbDirective?.dataentries?.length | regionAccordionTooltipTextPipe : 'regionalFeatures',
-      iavNgIf: iavDbDirective?.dataentries?.length,
-      content: regionalFeaturesTmpl
-    }">
-    </ng-container>
-
-    <!-- Connectivity -->
-    <ng-template #connectivityContentTmpl let-expansionPanel="expansionPanel">
-      <mat-card-content class="flex-grow-1 flex-shrink-1 w-100">
-        <ng-container *ngFor="let region of selectedRegions$ | async">
-          <connectivity-browser class="pe-all flex-shrink-1"
-            [region]="region"
-            [parcellationId]="selectedParcellation['@id']"
-            (setOpenState)="expansionPanel.expanded = $event"
-            (connectivityNumberReceived)="connectivityNumber = $event"
-            (connectivityLoadUrl)="connectivityLoadUrl = $event"
-            [accordionExpanded]="expansionPanel.expanded">
-          </connectivity-browser>
-        </ng-container>
-      </mat-card-content>
-    </ng-template>
-
-    <ng-container *ngTemplateOutlet="ngMatAccordionTmpl; context: {
-      title: 'Connectivity',
-      desc: connectivityNumber? connectivityNumber : connectedCounterDir.value,
-      iconClass: 'fas fa-braille',
-      iconTooltip: connectedCounterDir.value | regionAccordionTooltipTextPipe : 'connectivity',
-      iavNgIf: selectedParcellation?.hasAdditionalViewMode?.includes('connectivity'),
-      content: connectivityContentTmpl
-    }">
-    </ng-container>
-
-    <div class="w-0 h-0"
-      iav-counter
-      #connectedCounterDir="iavCounter">
-
-      <hbp-connectivity-matrix-row *ngIf="region && region.name"
-        [region]="region.name + (region.status? ' - ' + region.status : '')"
-        (connectivityDataReceived)="connectedCounterDir.value = $event.detail.length"
-        class="invisible d-block h-0 w-0"
-        [loadurl]="connectivityLoadUrl">
-      </hbp-connectivity-matrix-row>
-    </div>
-  </mat-accordion>
-</ng-template>
-
-<!-- overlay templates -->
-
-<!-- slice view overlay tmpl -->
-<ng-template #ngPanelOverlayTmpl let-panelIndex="panelIndex">
-
-  <!-- perspective view tmpl -->
-  <ng-template #overlayPerspectiveTmpl>
-    <layout-floating-container class="tmp" landmarkContainer>
-
-      <div class="d-flex flex-column justify-content-center align-items-center w-100 h-100 position-absolute opacity-crossfade screen-overlay pe-none"
-        [ngClass]="{onHover: !!(showPerpsectiveScreen$ | async)}"
-        [attr.id]="ID_MESH_LOADING_STATUS"
-        role="status">
-
-        <spinner-cmp *ngIf="showPerpsectiveScreen$ | async">
-        </spinner-cmp>
-        
-        <mat-list>
-          <mat-list-item>
-            {{ showPerpsectiveScreen$ | async }}
-          </mat-list-item>
-        </mat-list>
-      </div>
-
-      <!-- maximise/minimise button -->
-      <ng-container *ngTemplateOutlet="panelCtrlTmpl; context: {
-        panelIndex: panelIndex,
-        visible: (hoveredPanelIndices$ | async) === panelIndex
-      }">
-      </ng-container>
-
-      <!-- mesh loading is still weird -->
-      <!-- if the precomputed server does not have the necessary fragment file, then the numberws will not collate -->
-      <div *ngIf="false && (perspectiveViewLoading$ | async)" class="loadingIndicator">
-        <spinner-cmp></spinner-cmp>
-
-        <div *ngIf="false" perspectiveLoadingText>
-          {{ perspectiveViewLoading$ | async }}
-        </div>
-      </div>
-    </layout-floating-container>
-  </ng-template>
-
-  <!-- nb this slice view is not suitable for perspective view! -->
-  <layout-floating-container *ngIf="panelIndex < 3; else overlayPerspectiveTmpl"
-    landmarkContainer
-    class="overflow-hidden">
-
-    <!-- customLandmarks -->
-    <landmark-2d-flat-cmp *ngFor="let lm of (customLandmarks$ | async | filterByProperty : 'showInSliceView')"
-      (mouseenter)="handleMouseEnterCustomLandmark(lm)"
-      (mouseleave)="handleMouseLeaveCustomLandmark(lm)"
-      [color]="lm.color || [255, 255, 255]"
-      [positionX]="getPositionX(panelIndex, lm)"
-      [positionY]="getPositionY(panelIndex, lm)"
-      [positionZ]="getPositionZ(panelIndex, lm)">
-
-    </landmark-2d-flat-cmp>
-
-    <!-- only show landmarks in slice views -->
-
-    <landmark-2d-flat-cmp *ngFor="let spatialData of (selectedPtLandmarks$ | async)"
-      (mouseenter)="handleMouseEnterLandmark(spatialData)"
-      (mouseleave)="handleMouseLeaveLandmark(spatialData)"
-      [highlight]="spatialData.highlight ? spatialData.highlight : false"
-      [positionX]="getPositionX(panelIndex, spatialData)"
-      [positionY]="getPositionY(panelIndex, spatialData)"
-      [positionZ]="getPositionZ(panelIndex, spatialData)">
-    </landmark-2d-flat-cmp>
-
-    <!-- maximise/minimise button -->
-    <ng-container *ngTemplateOutlet="panelCtrlTmpl; context: {
-      panelIndex: panelIndex,
-      visible: (hoveredPanelIndices$ | async) === panelIndex
-    }">
-    </ng-container>
-
-    <div *ngIf="(sliceViewLoadingMain$ | async)[panelIndex]" class="loadingIndicator">
-      <spinner-cmp></spinner-cmp>
-    </div>
-  </layout-floating-container>
-
-</ng-template>
-
-<!-- panel control template -->
-<ng-template
-  #panelCtrlTmpl
-  let-panelIndex="panelIndex"
-  let-visible="visible">
-
-  <div class="opacity-crossfade always-show-touchdevice pe-all overlay-btn-container"
-    [ngClass]="{ onHover: visible }"
-    [attr.data-viewer-controller-visible]="visible"
-    [attr.data-viewer-controller-index]="panelIndex">
-
-    <!-- perspective specific control -->
-    <ng-container *ngIf="panelIndex === 3">
-      <!-- viewer ctrl -->
-      <mat-menu #viewerCtrlMenu>
-        <!-- NB must not lazy load. key listener needs to work even when component is not yet rendered -->
-        <viewer-ctrl-component class="d-block m-2"
-          [iav-key-listener]="[{ type: 'keydown', key: 'q', target: 'document', capture: true }]"
-          (iav-key-event)="viewerCtrlCmp.toggleParcVsbl()"
-          #viewerCtrlCmp="viewerCtrlCmp">
-        </viewer-ctrl-component>
-      </mat-menu>
-      <button mat-icon-button color="primary"
-        [matMenuTriggerFor]="viewerCtrlMenu">
-        <i class="fas fa-cog"></i>
-      </button>
-
-      <ng-container *ngTemplateOutlet="perspectiveOctantRemovalTmpl; context: {
-        state: (nehubaViewerPerspectiveOctantRemoval$ | async),
-        disableOctantRemoval: disableOctantRemoval$ | async
-      }">
-
-      </ng-container>
-    </ng-container>
-
-    <!-- factor < 1.0 === zoom in -->
-    <button mat-icon-button color="primary"
-      (click)="zoomNgView(panelIndex, 0.9)"
-      [attr.aria-label]="ARIA_LABEL_ZOOM_IN">
-      <i class="fas fa-search-plus"></i>
-    </button>
-
-    <!-- factor > 1.0 === zoom out -->
-    <button mat-icon-button color="primary"
-      (click)="zoomNgView(panelIndex, 1.1)"
-      [attr.aria-label]="ARIA_LABEL_ZOOM_OUT">
-      <i class="fas fa-search-minus"></i>
-    </button>
-
-    <maximise-panel-button
-      (click)="toggleMaximiseMinimise(panelIndex)"
-      [touch-side-class]="panelIndex">
-    </maximise-panel-button>
-  </div>
-
-</ng-template>
-
-
-<ng-template #perspectiveOctantRemovalTmpl let-state="state" let-disableOctantRemoval="disableOctantRemoval">
-  <div class="d-inline-block"
-    [matTooltip]="disableOctantRemoval?.mode !== null ? disableOctantRemoval.message : null">
-    <button
-      (click)="setOctantRemoval(!state)"
-      mat-icon-button
-      [disabled]="disableOctantRemoval?.mode !== null"
-      [attr.aria-label]="ARIA_LABEL_TOGGLE_FRONTAL_OCTANT"
-      color="primary">
-
-      <!-- octant removal is true -->
-      <ng-template [ngIf]="disableOctantRemoval?.mode !== null ? disableOctantRemoval.mode : state" [ngIfElse]="octantRemovalOffTmpl">
-        <i class="fas fa-eye-slash"></i>
-      </ng-template>
-
-      <!-- octant removal is false -->
-      <ng-template #octantRemovalOffTmpl>
-        <i class="fas fa-eye"></i>
-      </ng-template>
-    </button>
-  </div>
-</ng-template>
-
-
-<!-- template for rendering tab -->
-<ng-template #tabTmpl
-  let-isOpen="isOpen"
-  let-regionSelected="regionSelected"
-  let-iavAdditionallayers="iavAdditionallayers">
-
-  <!-- if mat drawer is open -->
-  <ng-template [ngIf]="isOpen" [ngIfElse]="tabTmpl_closedTmpl">
-    <ng-container *ngTemplateOutlet="tabTmpl_defaultTmpl; context: {
-      matColor: 'basic',
-      fontIcon: 'fa-chevron-left'
-    }">
-    </ng-container>
-  </ng-template>
-
-  <!-- if matdrawer is closed -->
-  <ng-template #tabTmpl_closedTmpl>
-
-    <!-- if additional layers are being shown -->
-    <ng-template [ngIf]="iavAdditionallayers?.length > 0" [ngIfElse]="tabTmpl_noAdditionalLayers">
-      <ng-container *ngTemplateOutlet="tabTmpl_defaultTmpl; context: {
-        matColor: 'accent',
-        fontIcon: 'fa-database',
-        tooltip: 'Explore dataset preview'
-      }">
-      </ng-container>
-    </ng-template>
-
-    <!-- if additional layers not not being shown -->
-    <ng-template #tabTmpl_noAdditionalLayers>
-
-      <!-- if region selected > 0 -->
-      <ng-template [ngIf]="regionSelected?.length > 0" [ngIfElse]="tabTmpl_nothingSelected">
-        <div class="hidden"
-          iav-region
-          [region]="regionSelected[0]"
-          #tabTmpl_iavRegion="iavRegion">
-        </div>
-
-        <ng-container *ngTemplateOutlet="tabTmpl_defaultTmpl; context: {
-          matColor: 'accent',
-          customColor: tabTmpl_iavRegion.rgbString,
-          customColorDarkmode: tabTmpl_iavRegion.rgbDarkmode,
-          fontIcon: 'fa-brain',
-          tooltip: 'Explore ' + tabTmpl_iavRegion.region.name
-        }">
-
-        </ng-container>
-      </ng-template>
-
-      <!-- nothing is selected -->
-      <ng-template #tabTmpl_nothingSelected>
-        <ng-container *ngTemplateOutlet="tabTmpl_defaultTmpl; context: {
-          matColor: 'primary',
-          fontIcon: 'fa-sitemap',
-          tooltip: 'Explore regions'
-        }">
-        </ng-container>
-      </ng-template>
-    </ng-template>
-  </ng-template>
-
-  <ng-template #tabTmpl_defaultTmpl
-    let-matColor="matColor"
-    let-fontIcon="fontIcon"
-    let-customColor="customColor"
-    let-customColorDarkmode="customColorDarkmode"
-    let-tooltip="tooltip">
-    <button mat-raised-button
-      [attr.aria-label]="ARIA_LABEL_TOGGLE_SIDE_PANEL"
-      [matTooltip]="tooltip"
-      class="pe-all tab-toggle"
-      [ngClass]="{
-        'darktheme': customColorDarkmode === true,
-        'lighttheme': customColorDarkmode === false
-      }"
-      [style.backgroundColor]="customColor"
-      (click)="sideNavMasterSwitch.toggle()"
-      [color]="(!customColor && matColor) ? matColor : null">
-
-      <span [ngClass]="{'iv-custom-comp  text': !!customColor}">
-        <i class="fas" [ngClass]="fontIcon || 'fa-question'"></i>
-      </span>
-    </button>
-  </ng-template>
-</ng-template>
diff --git a/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.spec.ts b/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.spec.ts
deleted file mode 100644
index f24c61fc2d74b143ee4554871bd29032dc2ab27e..0000000000000000000000000000000000000000
--- a/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.spec.ts
+++ /dev/null
@@ -1,94 +0,0 @@
-import { TestBed, async, fakeAsync, tick } from "@angular/core/testing"
-import { CommonModule, DOCUMENT } from "@angular/common"
-import { NehubaViewerUnit, IMPORT_NEHUBA_INJECT_TOKEN } from "./nehubaViewer.component"
-import { importNehubaFactory } from "../util"
-import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service"
-import { LoggingModule } from "src/logging"
-import { APPEND_SCRIPT_TOKEN, appendScriptFactory } from "src/util/constants"
-
-
-describe('nehubaViewer.component,ts', () => {
-  describe('NehubaViewerUnit', () => {
-    beforeEach(async(() => {
-      TestBed.configureTestingModule({
-        imports: [
-          CommonModule,
-          LoggingModule
-        ],
-        declarations: [
-          NehubaViewerUnit
-        ],
-        providers:[
-          {
-            provide: IMPORT_NEHUBA_INJECT_TOKEN,
-            useFactory: importNehubaFactory,
-            deps: [ APPEND_SCRIPT_TOKEN ]
-          },
-          {
-            provide: APPEND_SCRIPT_TOKEN,
-            useFactory: appendScriptFactory,
-            deps: [ DOCUMENT ]
-          },
-          AtlasWorkerService
-        ]
-      }).compileComponents()
-    }))
-
-    it('> creates component', () => {
-      const fixture = TestBed.createComponent(NehubaViewerUnit)
-      expect(fixture.componentInstance).toBeTruthy()
-    })
-
-    describe('> getters', () => {
-      it('> showLayersName is a combination of multiNgIdsLabelIndexMap key values and overrideShowLayers', () => {
-        const fixture = TestBed.createComponent(NehubaViewerUnit)
-        const overrideShowLayers = [
-          `test-1`,
-          `test-2`
-        ]
-        fixture.componentInstance.overrideShowLayers = overrideShowLayers
-        fixture.componentInstance.multiNgIdsLabelIndexMap = new Map([
-          ['test-3', new Map()]
-        ])
-
-        fixture.detectChanges()
-
-        expect(fixture.componentInstance.showLayersName).toEqual([
-          `test-1`,
-          `test-2`,
-          `test-3`
-        ])
-      })
-    })
-
-    describe('> on create', () => {
-      it('> calls onInit lifecycle param properly', () => {
-        const onInitSpy = jasmine.createSpy('onInit')
-        const fixture = TestBed.createComponent(NehubaViewerUnit)
-        fixture.componentInstance.lifecycle = {
-          onInit: onInitSpy
-        }
-
-        fixture.detectChanges()
-
-        expect(onInitSpy).toHaveBeenCalled()
-      })
-    })
-
-    describe('> loading meshes', () => {
-      it('> on loadMeshes$ emit, calls nehubaViewer.setMeshesToLoad', fakeAsync(() => {
-
-        const fixture = TestBed.createComponent(NehubaViewerUnit)
-        fixture.componentInstance.nehubaViewer = {
-          setMeshesToLoad: jasmine.createSpy('setMeshesToLoad').and.returnValue(null),
-          dispose: () => {}
-        }
-
-        fixture.detectChanges()
-        fixture.componentInstance['loadMeshes']([1,2,3], { name: 'foo-bar' })
-        tick(1000)
-        expect(fixture.componentInstance.nehubaViewer.setMeshesToLoad).toHaveBeenCalledWith([1,2,3], { name: 'foo-bar' })
-      }))
-    })
-  })
-})
diff --git a/src/ui/nehubaContainer/nehubaViewerInterface/nehubaViewerInterface.directive.spec.ts b/src/ui/nehubaContainer/nehubaViewerInterface/nehubaViewerInterface.directive.spec.ts
deleted file mode 100644
index 91c8e5847a0b025b882057e7a2255fcad7bb5f7a..0000000000000000000000000000000000000000
--- a/src/ui/nehubaContainer/nehubaViewerInterface/nehubaViewerInterface.directive.spec.ts
+++ /dev/null
@@ -1,148 +0,0 @@
-import { CommonModule } from "@angular/common"
-import { Component } from "@angular/core"
-import { TestBed, async, ComponentFixture } from "@angular/core/testing"
-import { By } from "@angular/platform-browser"
-import { BrowserDynamicTestingModule } from "@angular/platform-browser-dynamic/testing"
-import { MockStore, provideMockStore } from "@ngrx/store/testing"
-import { ngViewerSelectorOctantRemoval } from "src/services/state/ngViewerState/selectors"
-import { NehubaViewerUnit } from "../nehubaViewer/nehubaViewer.component"
-import { NehubaViewerContainerDirective } from "./nehubaViewerInterface.directive"
-import { viewerStateSelectorNavigation, viewerStateStandAloneVolumes } from "src/services/state/viewerState/selectors";
-import { Subject } from "rxjs"
-import { ngViewerActionNehubaReady } from "src/services/state/ngViewerState/actions"
-
-describe('> nehubaViewerInterface.directive.ts', () => {
-  describe('> NehubaViewerContainerDirective', () => {
-
-    @Component({
-      template: ''
-    })
-    class DummyCmp{}
-
-    beforeEach(async(() => {
-      TestBed.configureTestingModule({
-        imports: [
-          
-        ],
-        declarations: [
-          NehubaViewerContainerDirective,
-          DummyCmp,
-          NehubaViewerUnit,
-        ],
-        providers: [
-          provideMockStore({ initialState: {} })
-        ]
-      }).overrideModule(BrowserDynamicTestingModule,{
-        set: {
-          entryComponents: [
-            NehubaViewerUnit
-          ]
-        }
-      }).overrideComponent(DummyCmp, {
-        set: {
-          template: `
-          <div iav-nehuba-viewer-container>
-          </div>
-          `
-        }
-      }).compileComponents()
-
-    }))
-
-    beforeEach(() => {
-      const mockStore = TestBed.inject(MockStore)
-      mockStore.overrideSelector(ngViewerSelectorOctantRemoval, true)
-      mockStore.overrideSelector(viewerStateStandAloneVolumes, [])
-      mockStore.overrideSelector(viewerStateSelectorNavigation, null)
-    })
-
-    it('> can be inited', () => {
-
-      const fixture = TestBed.createComponent(DummyCmp)
-      fixture.detectChanges()
-      const directive = fixture.debugElement.query(
-        By.directive(NehubaViewerContainerDirective)
-      )
-
-      expect(directive).toBeTruthy()
-    })
-
-    describe('> on createNehubaInstance', () => {
-      let fixture: ComponentFixture<DummyCmp>
-      let directiveInstance: NehubaViewerContainerDirective
-      let nehubaViewerInstanceSpy: jasmine.Spy
-      let elClearSpy: jasmine.Spy
-      let elCreateComponentSpy: jasmine.Spy
-      const spyNehubaViewerInstance = {
-        config: null,
-        lifecycle: null,
-        templateId: null,
-        errorEmitter: new Subject(),
-        debouncedViewerPositionChange: new Subject(),
-        layersChanged: new Subject(),
-        nehubaReady: new Subject(),
-        mouseoverSegmentEmitter: new Subject(),
-        mouseoverLandmarkEmitter: new Subject(),
-        mouseoverUserlandmarkEmitter: new Subject(),
-        elementRef: {
-          nativeElement: {}
-        }
-      }
-      const spyComRef = {
-        destroy: jasmine.createSpy('destroy')
-      }
-
-      beforeEach(() => {
-        fixture = TestBed.createComponent(DummyCmp)
-        const directive = fixture.debugElement.query(
-          By.directive(NehubaViewerContainerDirective)
-        )
-        
-        directiveInstance = directive.injector.get(NehubaViewerContainerDirective)
-        
-        nehubaViewerInstanceSpy = spyOnProperty(directiveInstance, 'nehubaViewerInstance').and.returnValue(spyNehubaViewerInstance)
-        elClearSpy = spyOn(directiveInstance['el'], 'clear')
-        // casting return value to any is not perfect, but since only 2 methods and 1 property is used, it's a quick way 
-        // rather than allow component to be created
-        elCreateComponentSpy = spyOn(directiveInstance['el'], 'createComponent').and.returnValue(spyComRef as any)
-      })
-
-      describe('> on createNehubaInstance called', () => {
-        const template = {}
-        const lifecycle = {}
-        beforeEach(() => {
-          directiveInstance.createNehubaInstance(template, lifecycle)
-        })
-        it('> method el.clear gets called before el.createComponent', () => {
-          expect(elClearSpy).toHaveBeenCalledBefore(elCreateComponentSpy)
-        })
-      })
-    
-      describe('> on clear called', () => {
-        it('> dispatches nehubaReady: false action', () => {
-          const mockStore = TestBed.inject(MockStore)
-          const mockStoreDispatchSpy = spyOn(mockStore, 'dispatch')
-          directiveInstance.clear()
-          expect(
-            mockStoreDispatchSpy
-          ).toHaveBeenCalledWith(
-            ngViewerActionNehubaReady({
-              nehubaReady: false
-            })
-          )
-        })
-
-        it('> iavNehubaViewerContainerViewerLoading emits false', () => {
-          const emitSpy = spyOn(directiveInstance.iavNehubaViewerContainerViewerLoading, 'emit')
-          directiveInstance.clear()
-          expect(emitSpy).toHaveBeenCalledWith(false)
-        })
-
-        it('> elClear called', () => {
-          directiveInstance.clear()
-          expect(elClearSpy).toHaveBeenCalled()
-        })
-      })
-    })
-  })
-})
diff --git a/src/ui/nehubaContainer/splashScreen/splashScreen.component.ts b/src/ui/nehubaContainer/splashScreen/splashScreen.component.ts
deleted file mode 100644
index 585083536b64287853ba3166840b32bb07021007..0000000000000000000000000000000000000000
--- a/src/ui/nehubaContainer/splashScreen/splashScreen.component.ts
+++ /dev/null
@@ -1,142 +0,0 @@
-import { AfterViewInit, Component, ElementRef, Pipe, PipeTransform, ViewChild } from "@angular/core";
-import { select, Store } from "@ngrx/store";
-import { fromEvent, Observable, Subject, Subscription, combineLatest } from "rxjs";
-import { bufferTime, filter, map, switchMap, take, withLatestFrom, shareReplay, startWith } from 'rxjs/operators'
-import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service";
-import { NEWVIEWER, IavRootStoreInterface } from "src/services/stateStore.service";
-import { viewerStateHelperStoreName, viewerStateSelectAtlas } from "src/services/state/viewerState.store.helper";
-import { PureContantService } from "src/util";
-
-@Component({
-  selector : 'ui-splashscreen',
-  templateUrl : './splashScreen.template.html',
-  styleUrls : [
-    `./splashScreen.style.css`,
-  ],
-})
-
-export class SplashScreen implements AfterViewInit {
-
-  public stillLoadingTemplates$: Observable<any[]>
-  public loadedTemplate$: Observable<any[]>
-
-  public loadedAtlases$: Observable<any[]>
-  public stillLoadingAtlases$: Observable<any[]>
-
-  @ViewChild('parentContainer', {read: ElementRef})
-  private parentContainer: ElementRef
-  public activatedTemplate$: Subject<any> = new Subject()
-
-  private subscriptions: Subscription[] = []
-
-  constructor(
-    private store: Store<IavRootStoreInterface>,
-    private constanceService: AtlasViewerConstantsServices,
-    private pureConstantService: PureContantService
-  ) {
-    this.loadedTemplate$ = this.store.pipe(
-      select('viewerState'),
-      select('fetchedTemplates'),
-      shareReplay(1),
-    )
-
-    this.stillLoadingTemplates$ = combineLatest(
-      this.constanceService.getTemplateEndpoint$.pipe(
-        startWith(null)
-      ),
-      this.loadedTemplate$.pipe(
-        startWith([])
-      )
-    ).pipe(
-      map(([templateEndpoints, loadedTemplates]) => {
-        if (templateEndpoints && Array.isArray(templateEndpoints)) {
-          // TODO this is not exactly correct
-          return templateEndpoints.slice(loadedTemplates.length)
-        } else {
-          return null
-        }
-      })
-    )
-
-    this.loadedAtlases$ = this.store.pipe(
-      select(state => state[viewerStateHelperStoreName]),
-      select(state => state.fetchedAtlases),
-      filter(v => !!v)
-    )
-
-    this.stillLoadingAtlases$ = this.loadedAtlases$.pipe(
-      map(arr => this.pureConstantService.totalAtlasesLength - arr.length),
-      map(num => Array(num >= 0 ? num : 0).fill(null))
-    )
-  }
-
-  public ngAfterViewInit() {
-
-    /**
-     * instead of blindly listening to click event, this event stream waits to see if user mouseup within 200ms
-     * if yes, it is interpreted as a click
-     * if no, user may want to select a text
-     */
-    /**
-     * TODO change to onclick listener
-     */
-    this.subscriptions.push(
-      fromEvent(this.parentContainer.nativeElement, 'mousedown').pipe(
-        filter((ev: MouseEvent) => ev.button === 0),
-        switchMap(() => fromEvent(this.parentContainer.nativeElement, 'mouseup').pipe(
-          bufferTime(200),
-          take(1),
-        )),
-        filter(arr => arr.length > 0),
-        withLatestFrom(this.activatedTemplate$),
-        map(([_, atlas]) => atlas),
-      ).subscribe(atlas => this.store.dispatch(
-        viewerStateSelectAtlas({ atlas })
-      )),
-    )
-  }
-
-  public selectTemplateParcellation(template, parcellation) {
-    this.store.dispatch({
-      type : NEWVIEWER,
-      selectTemplate : template,
-      selectParcellation : parcellation,
-    })
-  }
-
-  public selectTemplate(template: any) {
-    this.store.dispatch({
-      type : NEWVIEWER,
-      selectTemplate : template,
-      selectParcellation : template.parcellations[0],
-    })
-  }
-
-  get totalTemplates() {
-    return this.constanceService.templateUrls.length
-  }
-}
-
-@Pipe({
-  name: 'getTemplateImageSrcPipe',
-})
-
-export class GetTemplateImageSrcPipe implements PipeTransform {
-  public transform(name: string): string {
-    return `./res/image/${name.replace(/[|&;$%@()+,\s./]/g, '')}.png`
-  }
-}
-
-@Pipe({
-  name: 'imgSrcSetPipe',
-})
-
-export class ImgSrcSetPipe implements PipeTransform {
-  public transform(src: string): string {
-    const regex = /^(.*?)(\.\w*?)$/.exec(src)
-    if (!regex) { throw new Error(`cannot find filename, ext ${src}`) }
-    const filename = regex[1]
-    const ext = regex[2]
-    return [100, 200, 300, 400].map(val => `${filename}-${val}${ext} ${val}w`).join(',')
-  }
-}
diff --git a/src/ui/parcellationRegion/regionMenu/regionMenu.component.ts b/src/ui/parcellationRegion/regionMenu/regionMenu.component.ts
deleted file mode 100644
index 8d9f0854522ede479b6ab101d2e535d1504932fa..0000000000000000000000000000000000000000
--- a/src/ui/parcellationRegion/regionMenu/regionMenu.component.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { Component, OnDestroy, Input } from "@angular/core";
-import { Store } from "@ngrx/store";
-import { Subscription } from "rxjs";
-import { RegionBase } from '../region.base'
-import { ARIA_LABELS } from 'common/constants'
-
-@Component({
-  selector: 'region-menu',
-  templateUrl: './regionMenu.template.html',
-  styleUrls: ['./regionMenu.style.css'],
-})
-export class RegionMenuComponent extends RegionBase implements OnDestroy {
-
-  private subscriptions: Subscription[] = []
-
-  constructor(
-    store$: Store<any>,
-  ) {
-    super(store$)
-  }
-
-  ngOnDestroy(): void {
-    this.subscriptions.forEach(s => s.unsubscribe())
-  }
-
-  @Input()
-  showRegionInOtherTmpl: boolean = true
-
-  SHOW_IN_OTHER_REF_SPACE = ARIA_LABELS.SHOW_IN_OTHER_REF_SPACE
-}
diff --git a/src/ui/parcellationRegion/regionMenu/regionMenu.template.html b/src/ui/parcellationRegion/regionMenu/regionMenu.template.html
deleted file mode 100644
index 65f9d1b40ad7a03247fd7248a62e396e05d281ba..0000000000000000000000000000000000000000
--- a/src/ui/parcellationRegion/regionMenu/regionMenu.template.html
+++ /dev/null
@@ -1,153 +0,0 @@
-<mat-card>
-  <!-- rgbDarkmode must be checked for strict equality to true/false 
-  as if rgb is undefined, rgbDarkmode will be null/undefined
-  which is falsy -->
-  <div class="header-container"
-    [ngClass]="{'darktheme': rgbDarkmode === true, 'lighttheme': rgbDarkmode === false}"
-    [style.backgroundColor]="rgbString">
-    <mat-card-title>
-      <div class="position-relative region-name iv-custom-comp text">
-        {{ region.name }}
-        <small *ngIf="region.status"> ({{region.status}})</small>
-      </div>
-    </mat-card-title>
-
-    <!-- subtitle on what it is -->
-    <mat-card-subtitle class="d-inline-flex align-items-center flex-wrap">
-      <mat-icon fontSet="fas" fontIcon="fa-brain"></mat-icon>
-      <span>
-        Brain region
-      </span>
-
-      <!-- origin datas format -->
-      <div *ngFor="let originDataset of (region.originDatasets || []); let index = index"
-        class="ml-2">
-        <i>&#183;</i>
-
-        <span *ngIf="originDataset?.format?.name as regionOrDsFormatName; else fallbackODsnameTmpl">
-          {{ regionOrDsFormatName }}
-        </span>
-        <ng-template #fallbackODsnameTmpl>
-          <span>
-            {{ regionOriginDatasetLabels$ | async | renderViewOriginDatasetlabel : index }}
-          </span>
-        </ng-template>
-
-        <span
-          aria-hidden="true"
-          [kgSchema]="originDataset.kgSchema"
-          [kgId]="originDataset.kgId"
-          single-dataset-directive
-          #sdDirective="singleDatasetDirective">
-        </span>
-
-        <ng-template [ngIf]="sdDirective.fetchFlag" [ngIfElse]="contentTmpl">
-          <spinner-cmp></spinner-cmp>
-        </ng-template>
-
-        <ng-template #contentTmpl>
-
-          <!-- fall back if no kg ref is available -->
-          <a *ngIf="sdDirective.kgReference.length === 0"
-            [href]="sdDirective.directLinkToKg"
-            target="_blank">
-            <button mat-icon-button
-              color="primary">
-              <i class="fas fa-external-link-alt"></i>
-            </button>
-          </a>
-
-          <!-- kg ref, normally doi -->
-          <a *ngFor="let kgRef of sdDirective.kgReference"
-            [href]="kgRef | doiParserPipe"
-            target="_blank">
-            <button mat-icon-button
-              color="primary">
-              <i class="fas fa-external-link-alt"></i>
-            </button>
-          </a>
-
-          <!-- pin/unpin -->
-          <ng-container *ngTemplateOutlet="pinTmpl; context: { $implicit: sdDirective.isFav$ | async }">
-          </ng-container>
-
-          <ng-template #pinTmpl let-isFav>
-
-            <button mat-icon-button
-              (click)="isFav ? sdDirective.undoableRemoveFav() : sdDirective.undoableAddFav()"
-              [color]="isFav ? 'primary' : 'default'">
-              <i class="fas fa-thumbtack"></i>
-            </button>
-          </ng-template>
-
-        </ng-template>
-      </div>
-
-      <mat-divider vertical="true" class="ml-2 h-2rem"></mat-divider>
-
-      <!-- position -->
-      <button mat-icon-button *ngIf="position"
-        (click)="navigateToRegion()"
-        [matTooltip]="GO_TO_REGION_CENTROID + ': ' + (position | nmToMm | addUnitAndJoin : 'mm')">
-        <mat-icon fontSet="fas" fontIcon="fa-map-marked-alt">
-        </mat-icon>
-      </button>
-
-      <!-- region in other templates -->
-      <button mat-icon-button
-        *ngIf="showRegionInOtherTmpl"
-        [attr.data-available-in-tmpl-count]="(regionInOtherTemplates$ | async).length"
-        [attr.aria-label]="AVAILABILITY_IN_OTHER_REF_SPACE"
-        [matMenuTriggerFor]="regionInOtherTemplatesMenu"
-        [matMenuTriggerData]="{ regionInOtherTemplates: regionInOtherTemplates$ | async }">
-        <i class="fas fa-globe"></i>
-      </button>
-
-    </mat-card-subtitle>
-
-  </div>
-</mat-card>
-
-<!-- ToDo make dynamic with AVAILABLE CONNECTIVITY DATASETS data - get info from atlas viewer core -->
-<mat-menu
-  #connectivitySourceDatasets="matMenu"
-  xPosition="before"
-  hasBackdrop="false">
-  <div>
-    <button mat-menu-item (mousedown)="showConnectivity(region.name)">
-      <span>1000 Brain Study - DTI connectivity</span>
-    </button>
-  </div>
-</mat-menu>
-
-<!-- template for switching template -->
-<mat-menu #regionInOtherTemplatesMenu="matMenu"
-  [aria-label]="SHOW_IN_OTHER_REF_SPACE">
-  <ng-template matMenuContent let-regionInOtherTemplates="regionInOtherTemplates">
-
-    <mat-list-item *ngFor="let sameRegion of regionInOtherTemplates; let i = index"
-      [attr.aria-label]="SHOW_IN_OTHER_REF_SPACE + ': ' + sameRegion.template.name + (sameRegion.hemisphere ? (' - ' + sameRegion.hemisphere) : '') "
-      (click)="changeView(sameRegion)"
-      mat-ripple
-      [attr.role]="'button'">
-      <mat-icon fontSet="fas" fontIcon="fa-none" mat-list-icon></mat-icon>
-      <div mat-line>
-        <ng-container *ngTemplateOutlet="regionInOtherTemplate; context: sameRegion">
-        </ng-container>
-      </div>
-    </mat-list-item>
-  </ng-template>
-</mat-menu>
-
-<!-- template for rendering template name and template hemisphere -->
-<ng-template #regionInOtherTemplate let-template="template" let-hemisphere="hemisphere">
-  <span class="overflow-x-hidden text-truncate"
-  [matTooltip]="template.name  + (hemisphere ? (' ' + hemisphere) : '')">
-    <span>
-      {{ template.name }}
-    </span>
-    <span *ngIf="hemisphere" class="text-muted">
-      ({{ hemisphere }})
-    </span>
-  </span>
-</ng-template>
diff --git a/src/ui/quickTour/arrowCmp/arrow.component.ts b/src/ui/quickTour/arrowCmp/arrow.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..20ba5136030656d9435a27ea71baf375691e9e8a
--- /dev/null
+++ b/src/ui/quickTour/arrowCmp/arrow.component.ts
@@ -0,0 +1,66 @@
+import { Component, HostBinding, Input, OnChanges } from "@angular/core";
+
+@Component({
+  selector: 'quick-tour-arrow',
+  templateUrl: './arrow.template.html',
+  styleUrls: [
+    './arrow.style.css'
+  ]
+})
+
+export class ArrowComponent implements OnChanges{
+
+  @HostBinding('style.transform')
+  transform = `translate(0px, 0px)`
+
+  stemStyle = {}
+
+  headTranslate = 'translate(0px, 0px)'
+
+  headStyle = {
+    transform: `rotate(0deg)`
+  }
+
+  @Input('quick-tour-arrow-from')
+  fromPos: [number, number]
+
+  @Input('quick-tour-arrow-to')
+  toPos: [number, number]
+
+  @Input('quick-tour-arrow-type')
+  type: 'straight' | 'concave-from-top' | 'concave-from-bottom' = 'straight'
+
+  ngOnChanges(){
+    let rotate: string
+    switch(this.type) {
+    case 'concave-from-top': {
+      rotate = '0deg'
+      break
+    }
+    case 'concave-from-bottom': {
+      rotate = '180deg'
+      break
+    }
+    default: {
+      rotate = `${(Math.PI / 2) + Math.atan2(
+        (this.toPos[1] - this.fromPos[1]),
+        (this.toPos[0] - this.fromPos[0])
+      )}rad`
+    }
+    }
+
+    this.transform = `translate(${this.fromPos[0]}px, ${this.fromPos[1]}px)`
+
+    this.headTranslate = `
+      translateX(-1.2rem)
+      translate(${this.toPos[0] - this.fromPos[0]}px, ${this.toPos[1] - this.fromPos[1]}px)
+      rotate(${rotate})
+    `
+    const x = (this.toPos[0] - this.fromPos[0]) / 100
+    const y = (this.toPos[1] - this.fromPos[1]) / 100
+
+    this.stemStyle = {
+      transform: `scale(${x}, ${y})`
+    }
+  }
+}
diff --git a/src/ui/quickTour/arrowCmp/arrow.style.css b/src/ui/quickTour/arrowCmp/arrow.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..5d4c5150c47ba915166d959e67a5f18c49ee289e
--- /dev/null
+++ b/src/ui/quickTour/arrowCmp/arrow.style.css
@@ -0,0 +1,49 @@
+:host
+{
+  width: 0px;
+  height: 0px;
+}
+
+:host-context([darktheme="true"]) svg
+{
+  stroke: rgb(200, 200, 200);
+}
+
+svg
+{
+  pointer-events: none;
+  stroke-width: 5px;
+  stroke: rgb(255, 255, 255);
+  stroke-linecap: round;
+  stroke-linejoin: round;
+}
+
+/* https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/vector-effect */
+svg path
+{
+  vector-effect: non-scaling-stroke;
+}
+
+#arrow_head
+{
+  width: 2.4rem;
+  height: 2.4rem;
+  transform-origin: 50% 0;
+  stroke-width: 3px;
+}
+
+#arrow_head #arrow_head_group
+{
+  transform-origin: 50% 10%;
+  transform: scale(0.95, 0.80);
+}
+
+#arrow_stem
+{
+  transform-origin: 0 0;
+  width: 100px;
+  height: 100px;
+  position: absolute;
+  left: 0;
+  top: 0;
+}
diff --git a/src/ui/quickTour/arrowCmp/arrow.template.html b/src/ui/quickTour/arrowCmp/arrow.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..b6dc3e8ac6da7af3ee3a3539648eb5dbaf142a44
--- /dev/null
+++ b/src/ui/quickTour/arrowCmp/arrow.template.html
@@ -0,0 +1,56 @@
+<!-- arrow head point to top -->
+
+<svg id="arrow_head"
+  [style.transform]="headTranslate"
+  fill="none"
+  viewBox="0 0 100 100"
+  xmlns="http://www.w3.org/2000/svg">
+  <g id="arrow_head_group">
+    <path id="arrrow_head_path" d="M.5 75.5c10-43 31-75 50-75s37 27 50 74"/></g>
+</svg>
+
+<!-- stem -->
+<ng-container [ngSwitch]="type">
+  <!-- arrow stem, bottom left top right, concave from top left -->
+  <ng-template [ngSwitchCase]="'concave-from-top'">
+    <svg id="arrow_stem"
+      [style]="stemStyle"
+      viewBox="0 0 100 100"
+      fill="none"
+      preserveAspectRatio="none"
+      xmlns="http://www.w3.org/2000/svg">
+      <g>
+        <path id="arrow_stem_concave_2" d="M0 0.499998C0 0.499998 26.0937 -0.22897 42 4C56.0254 7.72889 65.0753 9.44672 76 19C89.5421 30.8421 92.3854 42.6124 97 60C100.968 74.9525 100 99.5 100 99.5" />
+      </g>
+    </svg>
+
+  </ng-template>
+  <ng-template [ngSwitchCase]="'concave-from-bottom'">
+    <svg id="arrow_stem"
+      [style]="stemStyle"
+      viewBox="0 0 100 100"
+      fill="none"
+      preserveAspectRatio="none"
+      xmlns="http://www.w3.org/2000/svg">
+      <g>
+        <path id="arrow_stem_concave_2" d="M0 0.499998C0 0.499998 26.0937 -0.22897 42 4C56.0254 7.72889 65.0753 9.44672 76 19C89.5421 30.8421 92.3854 42.6124 97 60C100.968 74.9525 100 99.5 100 99.5" />
+      </g>
+    </svg>
+
+  </ng-template>
+
+  <!-- arrow stem, straight -->
+  <ng-template ngSwitchDefault>
+    <svg id="arrow_stem"
+      [style]="stemStyle"
+      viewBox="0 0 100 100"
+      fill="none"
+      preserveAspectRatio="none"
+      xmlns="http://www.w3.org/2000/svg">
+
+      <g id="arrow_stem_straight">
+        <path id="arrow_stem_straight_path" d="M0 0 100 100" />
+      </g>
+    </svg>
+  </ng-template>
+</ng-container>
diff --git a/src/ui/quickTour/constrants.ts b/src/ui/quickTour/constrants.ts
new file mode 100644
index 0000000000000000000000000000000000000000..38bb2d62b6443ac07caf3f8ff2a3217269f36a35
--- /dev/null
+++ b/src/ui/quickTour/constrants.ts
@@ -0,0 +1,37 @@
+import { InjectionToken, TemplateRef } from "@angular/core"
+
+type TPosition = 'top' | 'top-right' | 'right' | 'bottom-right' | 'bottom' | 'bottom-left' | 'left' | 'top-left' | 'center'
+
+type TCustomPosition = {
+    left: number
+    top: number
+}
+
+export interface IQuickTourData {
+    order: number
+    description: string
+    tourPosition?: TPosition
+    overwritePosition?: IQuickTourOverwritePosition
+    overwriteArrow?: TemplateRef<any> | string
+}
+
+export interface IQuickTourOverwritePosition {
+    dialog: TCustomPosition
+    arrow: TCustomPosition
+}
+
+export type TQuickTourPosition = TPosition
+
+export const QUICK_TOUR_CMP_INJTKN = new InjectionToken('QUICK_TOUR_CMP_INJTKN')
+
+export enum EnumQuickTourSeverity {
+    LOW = 'low',
+    MEDIUM = 'medium',
+    HIGH = 'hight',
+}
+
+export const PERMISSION_DIALOG_ACTIONS = {
+  START: `start`,
+  CANCEL: `cancel`,
+  NOTNOW: `notnow`,
+}
\ No newline at end of file
diff --git a/src/ui/quickTour/index.ts b/src/ui/quickTour/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8208c2cc7010ca2395c5b205369bef4784822946
--- /dev/null
+++ b/src/ui/quickTour/index.ts
@@ -0,0 +1,11 @@
+export {
+  QuickTourModule
+} from './module'
+
+export {
+  QuickTourThis
+} from './quickTourThis.directive'
+
+export {
+  IQuickTourData
+} from './constrants'
diff --git a/src/ui/quickTour/module.ts b/src/ui/quickTour/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..458170c31bfa501f71feaad8e8255412001358e7
--- /dev/null
+++ b/src/ui/quickTour/module.ts
@@ -0,0 +1,48 @@
+import { FullscreenOverlayContainer, OverlayContainer } from "@angular/cdk/overlay";
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { UtilModule } from "src/util";
+import { AngularMaterialModule } from "../sharedModules/angularMaterial.module";
+import { QuickTourThis } from "src/ui/quickTour/quickTourThis.directive";
+import { QuickTourService } from "src/ui/quickTour/quickTour.service";
+import { QuickTourComponent } from "src/ui/quickTour/quickTourComponent/quickTour.component";
+import { QuickTourDirective } from "src/ui/quickTour/quickTour.directive";
+import { ArrowComponent } from "./arrowCmp/arrow.component";
+import { WindowResizeModule } from "src/util/windowResize";
+import { QUICK_TOUR_CMP_INJTKN } from "./constrants";
+import { ComponentsModule } from "src/components";
+import {StartTourDialogDialog} from "src/ui/quickTour/startTourDialog/startTourDialog.component";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    AngularMaterialModule,
+    UtilModule,
+    WindowResizeModule,
+    ComponentsModule,
+  ],
+  declarations:[
+    QuickTourThis,
+    QuickTourComponent,
+    QuickTourDirective,
+    ArrowComponent,
+    StartTourDialogDialog
+  ],
+  exports: [
+    QuickTourDirective,
+    QuickTourThis,
+  ],
+  providers:[
+    {
+      provide: OverlayContainer,
+      useClass: FullscreenOverlayContainer
+    },
+    QuickTourService,
+    {
+      provide: QUICK_TOUR_CMP_INJTKN,
+      useValue: QuickTourComponent
+    }
+  ],
+  entryComponents: [ StartTourDialogDialog ]
+})
+export class QuickTourModule{}
diff --git a/src/ui/quickTour/quickTour.directive.ts b/src/ui/quickTour/quickTour.directive.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4b593a895cffd7237eadde787a35e4dc2815c1a8
--- /dev/null
+++ b/src/ui/quickTour/quickTour.directive.ts
@@ -0,0 +1,25 @@
+import { Directive, HostListener } from "@angular/core";
+import { QuickTourService } from "./quickTour.service";
+
+@Directive({
+  selector: '[quick-tour-opener]'
+})
+
+export class QuickTourDirective {
+
+  constructor(
+    private quickTourService: QuickTourService
+  ){}
+
+    @HostListener('window:keydown', ['$event'])
+  keyListener(ev: KeyboardEvent){
+    if (ev.key === 'Escape') {
+      this.quickTourService.endTour()
+    }
+  }
+
+    @HostListener('click')
+    onClick(){
+      this.quickTourService.startTour()
+    }
+}
diff --git a/src/ui/quickTour/quickTour.service.ts b/src/ui/quickTour/quickTour.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3853a19497de7f3494d77c587b1a1e044cf00f5c
--- /dev/null
+++ b/src/ui/quickTour/quickTour.service.ts
@@ -0,0 +1,143 @@
+import { ComponentRef, Inject, Injectable } from "@angular/core";
+import { BehaviorSubject, Subject } from "rxjs";
+import { Overlay, OverlayRef } from "@angular/cdk/overlay";
+import { ComponentPortal } from "@angular/cdk/portal";
+import { QuickTourThis } from "./quickTourThis.directive";
+import { DoublyLinkedList, IDoublyLinkedItem } from 'src/util'
+import { EnumQuickTourSeverity, PERMISSION_DIALOG_ACTIONS, QUICK_TOUR_CMP_INJTKN } from "./constrants";
+import { LOCAL_STORAGE_CONST } from "src/util/constants";
+import { MatDialog, MatDialogRef } from "@angular/material/dialog";
+import { StartTourDialogDialog } from "src/ui/quickTour/startTourDialog/startTourDialog.component";
+
+@Injectable()
+export class QuickTourService {
+
+  private overlayRef: OverlayRef
+  private cmpRef: ComponentRef<any>
+
+  public currSlideNum: number = null
+  public currentTip$: BehaviorSubject<IDoublyLinkedItem<QuickTourThis>> = new BehaviorSubject(null)
+  public detectChanges$: Subject<null> = new Subject()
+
+  public currActiveSlide: IDoublyLinkedItem<QuickTourThis>
+  public slides = new DoublyLinkedList<QuickTourThis>()
+
+  private startTourDialogRef: MatDialogRef<any>
+
+  public autoStartTriggered = false
+
+  constructor(
+    private overlay: Overlay,
+    /**
+     * quickTourService cannot directly reference quickTourComponent
+     * since quickTourComponent DI quickTourService
+     * makes sense, since we want to keep the dependency of svc on cmp as loosely (or non existent) as possible
+     */
+    @Inject(QUICK_TOUR_CMP_INJTKN) private quickTourCmp: any,
+    private matDialog: MatDialog
+  ){
+  }
+
+  public register(dir: QuickTourThis) {
+    this.slides.insertAfter(
+      dir,
+      linkedItem => {
+        const nextItem = linkedItem.next
+        if (nextItem && nextItem.thisObj.order < dir.order) {
+          return false
+        }
+        return linkedItem.thisObj.order < dir.order
+      }
+    )
+
+    
+    if (dir.quickTourSeverity === EnumQuickTourSeverity.MEDIUM || dir.quickTourSeverity === EnumQuickTourSeverity.HIGH) {
+      this.autoStart()
+    }
+  }
+
+  public unregister(dir: QuickTourThis){
+    this.slides.remove(dir)
+  }
+
+  autoStart() {
+
+    // if already viewed quick tour, return
+    if (localStorage.getItem(LOCAL_STORAGE_CONST.QUICK_TOUR_VIEWED)){
+      return
+    }
+    // if auto start already triggered, return
+    if (this.autoStartTriggered) return
+    this.autoStartTriggered = true
+    this.startTourDialogRef = this.matDialog.open(StartTourDialogDialog)
+    this.startTourDialogRef.afterClosed().subscribe(res => {
+      switch (res) {
+      case PERMISSION_DIALOG_ACTIONS.START:
+        this.startTour()
+        localStorage.setItem(LOCAL_STORAGE_CONST.QUICK_TOUR_VIEWED, 'true')
+        break
+      case PERMISSION_DIALOG_ACTIONS.CANCEL:
+        localStorage.setItem(LOCAL_STORAGE_CONST.QUICK_TOUR_VIEWED, 'true')
+        break
+      }
+    })
+
+  }
+
+  public startTour() {
+    if (!this.overlayRef) {
+      this.overlayRef = this.overlay.create({
+        height: '0px',
+        width: '0px',
+        hasBackdrop: true,
+        backdropClass: ['pe-none', 'cdk-overlay-dark-backdrop'],
+        positionStrategy: this.overlay.position().global(),
+      })
+    }
+
+    if (!this.cmpRef) {
+      this.cmpRef = this.overlayRef.attach(
+        new ComponentPortal(this.quickTourCmp)
+      )
+
+      this.currActiveSlide = this.slides.first
+      this.currentTip$.next(this.currActiveSlide)
+    }
+  }
+
+  public endTour() {
+    if (this.overlayRef) {
+      this.overlayRef.dispose()
+      this.overlayRef = null
+      this.cmpRef = null
+    }
+  }
+
+  public nextSlide() {
+    if (!this.currActiveSlide.next) return
+    this.currActiveSlide = this.currActiveSlide.next
+    this.currentTip$.next(this.currActiveSlide)
+  }
+
+  public previousSlide() {
+    if (!this.currActiveSlide.prev) return
+    this.currActiveSlide = this.currActiveSlide.prev
+    this.currentTip$.next(this.currActiveSlide)
+  }
+
+  public ff(index: number) {
+    try {
+      const slide = this.slides.get(index)
+      this.currActiveSlide = slide
+      this.currentTip$.next(slide)
+    } catch (_e) {
+      console.warn(`cannot find slide with index ${index}`)
+    }
+  }
+
+  changeDetected(dir: QuickTourThis) {
+    if (this.currActiveSlide?.thisObj === dir) {
+      this.detectChanges$.next(null)
+    }
+  }
+}
diff --git a/src/ui/quickTour/quickTourComponent/quickTour.component.ts b/src/ui/quickTour/quickTourComponent/quickTour.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..37c8dbb60827011899b04578f7eb6ddde51d382f
--- /dev/null
+++ b/src/ui/quickTour/quickTourComponent/quickTour.component.ts
@@ -0,0 +1,368 @@
+import {
+  Component,
+  ElementRef,
+  OnDestroy,
+  SecurityContext,
+  TemplateRef,
+  ViewChild,
+} from "@angular/core";
+import { combineLatest, fromEvent, Subscription } from "rxjs";
+import { QuickTourService } from "../quickTour.service";
+import { debounceTime, map, shareReplay } from "rxjs/operators";
+import { DomSanitizer } from "@angular/platform-browser";
+import { QuickTourThis } from "../quickTourThis.directive";
+import { clamp } from "src/util/generator";
+
+@Component({
+  templateUrl : './quickTour.template.html',
+  styleUrls : [
+    './quickTour.style.css'
+  ],
+})
+export class QuickTourComponent implements OnDestroy{
+
+  static TourCardMargin = 24
+  static TourCardWidthPx = 256
+  static TourCardHeightPx = 64
+  static TourCardWindowMargin = 8
+
+  @ViewChild('quickTourDialog', { read: ElementRef })
+  private quickTourDialog: ElementRef
+
+  public tourCardWidth = `${QuickTourComponent.TourCardWidthPx}px`
+  public arrowTmpl: TemplateRef<any>
+  public arrowSrc: string
+
+  public tourCardTransform = `translate(-500px, -500px)`
+  public customArrowTransform = `translate(-500px, -500px)`
+
+  public arrowFrom: [number, number] = [0, 0]
+  public arrowTo: [number, number] = [0, 0]
+  public arrowType: 'straight' | 'concave-from-top' | 'concave-from-bottom' = 'straight'
+
+  private subscriptions: Subscription[] = []
+  private currTipLinkedObj$ = this.quickTourService.currentTip$.pipe(
+    shareReplay(1)
+  )
+
+  public isLast$ = this.currTipLinkedObj$.pipe(
+    map(val => !val.next)
+  )
+
+  public isFirst$ = this.currTipLinkedObj$.pipe(
+    map(val => !val.prev)
+  )
+
+  public description$ = this.currTipLinkedObj$.pipe(
+    map(val => val.thisObj.description)
+  )
+
+  public descriptionMd$ = this.currTipLinkedObj$.pipe(
+    map(val => val.thisObj.descriptionMd)
+  )
+
+  private quickTourSize$ = this.currTipLinkedObj$.pipe(
+    map(val => val.list.size())
+  )
+
+  private quickTourIdx$ = this.currTipLinkedObj$.pipe(
+    map(val => val.index)
+  )
+
+  public quickTourProgress$ = combineLatest([
+    this.quickTourSize$,
+    this.quickTourIdx$
+  ]).pipe(
+    map(([ size, idx ]) => {
+      return Array(size).fill(false).map((_, index) => index === idx ? 'active' : _)
+    })
+  )
+
+  public overwrittenPosition = this.currTipLinkedObj$.pipe(
+    map(val => val.thisObj.overwritePosition)
+  )
+
+  private currTip: QuickTourThis
+
+  constructor(
+    public quickTourService: QuickTourService,
+    private sanitizer: DomSanitizer,
+  ) {
+
+    this.subscriptions.push(
+      
+      this.quickTourService.detectChanges$.pipe(
+        /**
+         * the debounce does two things:
+         * - debounce expensive calculate transform call
+         * - allow change detection to finish rendering element
+         */
+        debounceTime(16)
+      ).subscribe(() => {
+        this.calculateTransforms()
+      }),
+
+      this.quickTourService.currentTip$.pipe(
+        /**
+         * subscriber is quite expensive.
+         * only calculate at most once every 16 ms
+         */
+        debounceTime(16)
+      ).subscribe(linkedObj => {
+        this.arrowTmpl = null
+        this.arrowSrc = null
+        this.currTip = null
+
+        if (!linkedObj) {
+          // exit quick tour?
+          return
+        }
+        this.currTip = linkedObj.thisObj
+        this.calculateTransforms()
+      }),
+
+      fromEvent(window, 'keydown', { capture: true }).subscribe((ev: KeyboardEvent) => {
+        if (ev.key === 'Escape') {
+          this.quickTourService.endTour()
+        }
+        if (ev.key === 'ArrowRight') {
+          this.quickTourService.nextSlide()
+          ev.stopPropagation()
+        }
+        if (ev.key === 'ArrowLeft') {
+          this.quickTourService.previousSlide()
+          ev.stopPropagation()
+        }
+      })
+    )
+  }
+
+  ngOnDestroy(){
+    while (this.subscriptions.length > 0) this.subscriptions.pop().unsubscribe()
+  }
+
+  nextSlide(){
+    this.quickTourService.nextSlide()
+  }
+
+  prevSlide(){
+    this.quickTourService.previousSlide()
+  }
+
+  ff(index: number){
+    this.quickTourService.ff(index)
+  }
+
+  endTour(){
+    this.quickTourService.endTour()
+  }
+
+  calculateTransforms() {
+    if (!this.currTip) {
+      return
+    }
+    const tip = this.currTip
+
+    if (tip.overWriteArrow) {
+      if (typeof tip.overWriteArrow === 'string') {
+        this.arrowSrc = tip.overWriteArrow
+      } else {
+        this.arrowTmpl = tip.overWriteArrow
+      }
+    }
+
+    if (tip.overwritePosition) {
+      const { dialog, arrow } = tip.overwritePosition
+      const { top: dialogTop, left: dialogLeft } = dialog
+      const {
+        top: arrowTop,
+        left: arrowLeft,
+      } = arrow
+      this.tourCardTransform = this.sanitizer.sanitize(
+        SecurityContext.STYLE,
+        `translate(${dialogLeft}, ${dialogTop})`
+      )
+      this.customArrowTransform = this.sanitizer.sanitize(
+        SecurityContext.STYLE,
+        `translate(${arrowLeft}, ${arrowTop})`
+      )
+      return
+    }
+
+    const { x: hostX, y: hostY, width: hostWidth, height: hostHeight } = tip.getHostPos()
+    const { innerWidth, innerHeight } = window
+
+    const { position: tipPosition } = this.currTip
+    const translate: {x: number, y: number} = { x: hostX + hostWidth / 2, y: hostY + hostHeight / 2 }
+
+    const hostCogX = hostX + hostWidth / 2
+    const hostCogY = hostY + hostHeight / 2
+
+    let calcedPos: string = ''
+
+    /**
+     * if position is unspecfied, try to figure out position
+     */
+    if (!tipPosition) {
+
+      // if host centre of grav is to the right of the screen
+      // position tour el to the left, otherwise, right
+      calcedPos += hostCogX > (innerWidth / 2)
+        ? 'left'
+        : 'right'
+
+      // if host centre of grav is to the bottom of the screen
+      // position tour el to the top, otherwise, bottom
+      calcedPos += hostCogY > (innerHeight / 2)
+        ? 'top'
+        : 'bottom'
+    }
+
+    /**
+     * if the directive specified where helper should appear
+     * set the offset directly
+     */
+
+    const usePosition = tipPosition || calcedPos
+
+    /**
+     * default behaviour: center
+     * overwrite if align keywords appear
+     */
+
+    if (usePosition.includes('top')) {
+      translate.y = hostY
+    }
+    if (usePosition.includes('bottom')) {
+      translate.y = hostY + hostHeight
+    }
+    if (usePosition.includes('left')) {
+      translate.x = hostCogX
+    }
+    if (usePosition.includes('right')) {
+      translate.x = hostCogX
+    }
+
+    /**
+     * set tour card transform
+     * set a given margin, so 
+     */
+    const { width: cmpWidth, height: cmpHeight } = this.quickTourDialog
+      ? (this.quickTourDialog.nativeElement as HTMLElement).getBoundingClientRect()
+      : {} as any
+
+    const tourCardMargin = QuickTourComponent.TourCardMargin
+    const tourCardWidth = cmpWidth || QuickTourComponent.TourCardWidthPx
+    const tourCardHeight = cmpHeight || QuickTourComponent.TourCardHeightPx
+    const tourCardWindowMargin = QuickTourComponent.TourCardWindowMargin
+
+    /**
+     * catch if element is off screen
+     * clamp it inside the viewport
+     */
+    const tourCardTranslate = [
+      clamp(translate.x, 0, innerWidth),
+      clamp(translate.y, 0, innerHeight),
+    ]
+    if (usePosition.includes('top')) {
+      tourCardTranslate[1] += -1 * tourCardMargin - tourCardHeight
+    }
+    if (usePosition.includes('bottom')) {
+      tourCardTranslate[1] += tourCardMargin
+    }
+    if (usePosition.includes('left')) {
+      tourCardTranslate[0] += -1 * tourCardMargin - tourCardWidth
+    }
+    if (usePosition.includes('right')) {
+      tourCardTranslate[0] += tourCardMargin
+    }
+
+    if (usePosition === 'center') {
+      tourCardTranslate[0] -= tourCardWidth /2
+      tourCardTranslate[1] -= tourCardHeight /2
+    }
+    tourCardTranslate[0] = clamp(
+      tourCardTranslate[0],
+      tourCardWindowMargin,
+      innerWidth - tourCardWidth - tourCardWindowMargin
+    )
+
+    tourCardTranslate[1] = clamp(
+      tourCardTranslate[1],
+      tourCardWindowMargin,
+      innerHeight - tourCardHeight - tourCardWindowMargin
+    )
+    this.tourCardTransform = `translate(${tourCardTranslate[0]}px, ${tourCardTranslate[1]}px)`
+
+    /**
+     * set arrow from / to
+     */
+    
+    const {
+      arrowTo
+    } = (() => {
+      if (usePosition.includes('top')) {
+        return {
+          arrowTo: [ hostCogX, hostY ]
+        }
+      }
+      if (usePosition.includes('bottom')) {
+        return {
+          arrowTo: [ hostCogX, hostY + hostHeight ]
+        }
+      }
+      if (usePosition.includes('left')) {
+        return {
+          arrowTo: [ hostX, hostCogY ]
+        }
+      }
+      if (usePosition.includes('right')) {
+        return {
+          arrowTo: [ hostX + hostWidth, hostCogY ]
+        }
+      }
+      return {
+        arrowTo: [0, 0]
+      }
+    })()
+
+
+    const arrowFrom = [ arrowTo[0], arrowTo[1] ]
+
+    if (usePosition.includes('top')) {
+      arrowFrom[1] -= tourCardMargin + (tourCardHeight / 2)
+      this.arrowType = 'concave-from-bottom'
+    }
+    if (usePosition.includes('bottom')) {
+      arrowFrom[1] += tourCardMargin + (tourCardHeight / 2)
+      this.arrowType = 'concave-from-top'
+    }
+    if (usePosition.includes('left')) {
+      arrowFrom[0] -= tourCardMargin
+      this.arrowType = 'straight'
+    }
+    if (usePosition.includes('right')) {
+      arrowFrom[0] += tourCardMargin
+      this.arrowType = 'straight'
+    }
+    this.arrowFrom = arrowFrom as [number, number]
+    this.arrowTo = arrowTo as [number, number]
+
+    /**
+     * set arrow type
+     */
+    
+    this.arrowType = 'straight'
+
+    if (usePosition.includes('top')) {
+      this.arrowType = 'concave-from-bottom'
+    }
+    if (usePosition.includes('bottom')) {
+      this.arrowType = 'concave-from-top'
+    }
+  }
+
+  handleWindowResize(){
+    this.calculateTransforms()
+  }
+}
diff --git a/src/ui/quickTour/quickTourComponent/quickTour.style.css b/src/ui/quickTour/quickTourComponent/quickTour.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..cf9d9de0e4d057209c09d6ff5461d5a2844d9708
--- /dev/null
+++ b/src/ui/quickTour/quickTourComponent/quickTour.style.css
@@ -0,0 +1,33 @@
+:host
+{
+  display: inline-flex;
+  width: 0px;
+  height: 0px;
+  position: relative;
+}
+
+mat-card
+{
+  position: absolute;
+  margin: 0;
+  z-index: 10;
+}
+
+.custom-svg >>> svg
+{
+  pointer-events: none;
+  stroke-width: 3px;
+  stroke: rgb(255, 255, 255);
+  stroke-linecap: round;
+  stroke-linejoin: round;
+}
+
+.progress-dot
+{
+  transition: opacity ease-in-out 300ms;
+}
+
+.progress-dot:hover
+{
+  opacity: 0.8!important;
+}
\ No newline at end of file
diff --git a/src/ui/quickTour/quickTourComponent/quickTour.template.html b/src/ui/quickTour/quickTourComponent/quickTour.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..fe745b9e97874dde047d83598ee3b625cfe8eac8
--- /dev/null
+++ b/src/ui/quickTour/quickTourComponent/quickTour.template.html
@@ -0,0 +1,75 @@
+<mat-card
+    iav-window-resize
+    [iav-window-resize-time]="64"
+    (iav-window-resize-event)="handleWindowResize()"
+    [style.width]="tourCardWidth"
+    [style.transform]="tourCardTransform"
+    #quickTourDialog>
+    <mat-card-content>
+        <markdown-dom *ngIf="descriptionMd$ | async as md; else plainTxtTmpl"
+            [markdown]="md">
+        </markdown-dom>
+        <ng-template #plainTxtTmpl>
+            {{ description$ | async }}
+        </ng-template>
+    </mat-card-content>
+    <mat-card-actions>
+        <button  mat-icon-button
+            (click)="prevSlide()"
+            [disabled]="isFirst$ | async"
+            matTooltip="Previous [LEFT ARROW]">
+            <i class="fas fa-chevron-left"></i>
+        </button>
+        <button mat-icon-button
+            (click)="nextSlide()"
+            [disabled]="isLast$ | async"
+            matTooltip="Next [RIGHT ARROW]">
+            <i class="fas fa-chevron-right"></i>
+        </button>
+
+        <!-- dismiss (last) -->
+        <ng-template [ngIf]="isLast$ | async" [ngIfElse]="notLastTmpl">
+            <button mat-stroked-button
+                color="primary"
+                (click)="endTour()"
+                matTooltip="Dismiss [ESC]">
+                <i class="m-1 fas fa-check"></i>
+                <span>complete</span>
+            </button>
+        </ng-template>
+
+        <!-- dismiss (not last) -->
+        <ng-template #notLastTmpl>
+            <button mat-icon-button
+                (click)="endTour()"
+                matTooltip="Dismiss [ESC]">
+                <i class="fas fa-times"></i>
+            </button>
+        </ng-template>
+
+        <!-- progress dots -->
+        <span class="muted d-inline-flex align-items-center">
+            <i *ngFor="let active of quickTourProgress$ | async; let index = index"
+                (click)="ff(index)"
+                [ngClass]="{ 'fa-xs muted-3': !active }"
+                class="ml-1 fas fa-circle cursor-pointer progress-dot"></i>
+        </span>
+    </mat-card-actions>
+</mat-card>
+
+<div *ngIf="arrowTmpl" [style.transform]="customArrowTransform"
+    class="custom-svg">
+    <ng-container *ngTemplateOutlet="arrowTmpl">
+    </ng-container>
+</div>
+
+<ng-template [ngIf]="arrowSrc">
+    arrow src not yet implmented
+</ng-template>
+
+<quick-tour-arrow
+    *ngIf="!arrowTmpl && !arrowSrc"
+    [quick-tour-arrow-to]="arrowTo"
+    [quick-tour-arrow-from]="arrowFrom"
+    [quick-tour-arrow-type]="arrowType">
+</quick-tour-arrow>
\ No newline at end of file
diff --git a/src/ui/quickTour/quickTourThis.directive.ts b/src/ui/quickTour/quickTourThis.directive.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1908166452147779e29c35e08d9838f60e1479bc
--- /dev/null
+++ b/src/ui/quickTour/quickTourThis.directive.ts
@@ -0,0 +1,48 @@
+import { Directive, ElementRef, Input, OnChanges, OnDestroy, OnInit, TemplateRef } from "@angular/core";
+import { QuickTourService } from "src/ui/quickTour/quickTour.service";
+import { EnumQuickTourSeverity, IQuickTourOverwritePosition, TQuickTourPosition } from "src/ui/quickTour/constrants";
+import {LOCAL_STORAGE_CONST} from "src/util/constants";
+
+@Directive({
+  selector: '[quick-tour]',
+  exportAs: 'quickTour'
+})
+export class QuickTourThis implements OnInit, OnChanges, OnDestroy {
+
+  @Input('quick-tour-order') order: number = 0
+  @Input('quick-tour-description') description: string = 'No description'
+  @Input('quick-tour-description-md') descriptionMd: string
+  @Input('quick-tour-position') position: TQuickTourPosition
+  @Input('quick-tour-overwrite-position') overwritePosition: IQuickTourOverwritePosition
+  @Input('quick-tour-overwrite-arrow') overWriteArrow: TemplateRef<any> | string
+  @Input('quick-tour-severity') quickTourSeverity: EnumQuickTourSeverity = EnumQuickTourSeverity.MEDIUM
+
+  private attachedTmpl: ElementRef
+
+  constructor(
+    private quickTourService: QuickTourService,
+    private el: ElementRef
+  ) {}
+
+  public getHostPos() {
+    const { x, y, width, height } = (this.attachedTmpl?.nativeElement || this.el.nativeElement as HTMLElement).getBoundingClientRect()
+    return { x, y, width, height }
+  }
+
+  ngOnInit() {
+    this.quickTourService.register(this)
+  }
+
+  ngOnChanges() {
+    this.quickTourService.changeDetected(this)
+  }
+
+  ngOnDestroy() {
+    this.quickTourService.unregister(this)
+  }
+
+  attachTo(tmp: ElementRef){
+    this.attachedTmpl = tmp
+    this.quickTourService.changeDetected(this)
+  }
+}
diff --git a/src/ui/quickTour/startTourDialog/startTourDialog.component.ts b/src/ui/quickTour/startTourDialog/startTourDialog.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ac77e1d479fa84a408d228f51ba6d1d65ddc39d7
--- /dev/null
+++ b/src/ui/quickTour/startTourDialog/startTourDialog.component.ts
@@ -0,0 +1,14 @@
+import {Component} from "@angular/core";
+import {MatDialogRef} from "@angular/material/dialog";
+import { CONST } from 'common/constants'
+import { PERMISSION_DIALOG_ACTIONS } from "../constrants";
+
+@Component({
+  selector: 'quick-tour-start-dialog',
+  templateUrl: './startTourDialog.template.html',
+})
+export class StartTourDialogDialog {
+  public CONST = CONST
+  public PERMISSION_DIALOG_ACTIONS = PERMISSION_DIALOG_ACTIONS
+  constructor(public dialogRef: MatDialogRef<StartTourDialogDialog>) {}
+}
diff --git a/src/ui/quickTour/startTourDialog/startTourDialog.template.html b/src/ui/quickTour/startTourDialog/startTourDialog.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..2f0db03ffac09e383c69ec1d4a62f60b0de33b72
--- /dev/null
+++ b/src/ui/quickTour/startTourDialog/startTourDialog.template.html
@@ -0,0 +1,34 @@
+<h3 mat-dialog-title>
+    {{ CONST.QUICKTOUR_HEADER }}
+</h3>
+
+<div mat-dialog-content>
+    <span>
+        {{ CONST.PERMISSION_TO_QUICKTOUR }}
+    </span>
+</div>
+
+<div mat-dialog-actions class="d-flex">
+    <button
+        mat-raised-button
+        color="primary"
+        [mat-dialog-close]="PERMISSION_DIALOG_ACTIONS.START">
+        <i class="fas fa-play"></i>
+        <span>
+            {{ CONST.QUICKTOUR_OK }}
+        </span>
+    </button>
+    <button
+        mat-stroked-button
+        [mat-dialog-close]="PERMISSION_DIALOG_ACTIONS.NOTNOW">
+        {{ CONST.QUICKTOUR_NEXTTIME }}
+    </button>
+    
+    <div class="flex-grow-1 flex-shrink-1"></div>
+
+    <button
+        mat-button
+        [mat-dialog-close]="PERMISSION_DIALOG_ACTIONS.CANCEL">
+        {{ CONST.QUICKTOUR_CANCEL }}
+    </button>
+</div>
diff --git a/src/ui/screenshot/index.ts b/src/ui/screenshot/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9bb724742f69501cfd9d0b330b56b32294f38e4e
--- /dev/null
+++ b/src/ui/screenshot/index.ts
@@ -0,0 +1,5 @@
+export { ScreenshotModule } from './module'
+export {
+  HANDLE_SCREENSHOT_PROMISE,
+  TypeHandleScrnShotPromise,
+} from './util'
diff --git a/src/ui/screenshot/module.ts b/src/ui/screenshot/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..68ca809f2ca6d93d53263f25d5ffa20430318c8b
--- /dev/null
+++ b/src/ui/screenshot/module.ts
@@ -0,0 +1,28 @@
+import { FullscreenOverlayContainer, OverlayContainer } from "@angular/cdk/overlay";
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { UtilModule } from "src/util";
+import { AngularMaterialModule } from "../sharedModules/angularMaterial.module";
+import { ScreenshotCmp } from "./screenshotCmp/screenshot.component";
+import { ScreenshotSwitch } from "./screenshotSwitch.directive";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    AngularMaterialModule,
+    UtilModule,
+  ],
+  declarations:[
+    ScreenshotSwitch,
+    ScreenshotCmp,
+  ],
+  exports: [
+    ScreenshotSwitch,
+  ],
+  providers:[{
+    provide: OverlayContainer,
+    useClass: FullscreenOverlayContainer
+  }]
+})
+
+export class ScreenshotModule{}
diff --git a/src/ui/screenshot/screenshotCmp/screenshot.component.ts b/src/ui/screenshot/screenshotCmp/screenshot.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9af9d6b90a7d60af3438b2784906918b6f74aab0
--- /dev/null
+++ b/src/ui/screenshot/screenshotCmp/screenshot.component.ts
@@ -0,0 +1,200 @@
+import { Component, HostListener, Inject, OnDestroy, Output, EventEmitter, Optional, ViewChild, TemplateRef } from "@angular/core";
+import { MatDialog } from "@angular/material/dialog";
+import { MatSnackBar } from "@angular/material/snack-bar";
+import { combineLatest, Observable, Subject, Subscription } from "rxjs";
+import { distinctUntilChanged, map, mapTo, shareReplay, startWith, switchMap, switchMapTo, tap } from "rxjs/operators";
+import { HANDLE_SCREENSHOT_PROMISE, TypeHandleScrnShotPromise } from "../util";
+import { getDateString } from 'common/util'
+import { getUuid } from "src/util/fn";
+
+const BORDER_WIDTH = 10000
+
+@Component({
+  templateUrl: './screenshot.template.html',
+  styleUrls: [
+    './screenshot.style.css'
+  ]
+})
+
+export class ScreenshotCmp implements OnDestroy{
+
+  public borderStyle = `${BORDER_WIDTH}px rgba(0, 0, 0, 0.5) solid`
+  private mousedown$ = new Subject<MouseEvent>()
+  private mouseup$ = new Subject<MouseEvent>()
+  private mousemove$ = new Subject<MouseEvent>()
+  private subscriptions: Subscription[] = []
+
+  public isDragging$: Observable<boolean>
+  public transformString$: Observable<string>
+  public widthString$: Observable<string>
+  public heightString$: Observable<string>
+
+  private flipRect$: Observable<{ x: boolean, y: boolean }>
+
+  constructor(
+    private dialog: MatDialog,
+    private snackbar: MatSnackBar,
+    @Optional() @Inject(HANDLE_SCREENSHOT_PROMISE) private handleScreenshotPromise: TypeHandleScrnShotPromise
+  ){
+    this.isDragging$ = this.mousedown$.pipe(
+      switchMapTo(this.mouseup$.pipe(
+        mapTo(false),
+        startWith(true)
+      )),
+      distinctUntilChanged(),
+    )
+
+    this.flipRect$ = this.mousedown$.pipe(
+      switchMap(mousedownEv => this.mousemove$.pipe(
+        map(mousemoveEv => {
+          return {
+            x: mousemoveEv.clientX < mousedownEv.clientX,
+            y: mousemoveEv.clientY < mousedownEv.clientY
+          }
+        })
+      )),
+      distinctUntilChanged(({ x: oldX, y: oldY }, {x, y}) => oldX === x && oldY === y),
+    )
+
+    this.transformString$ = combineLatest([
+      this.mousedown$,
+      this.flipRect$,
+    ]).pipe(
+      map(([ev, { x, y }]) => {
+        /**
+         * scale will retroactively change the effect of previous translations
+         */
+        const xFactor = x ? -1 : 1
+        const yFactor = y ? -1 : 1
+        return `translate(${-BORDER_WIDTH * xFactor}px, ${-BORDER_WIDTH * yFactor}px) scale(${xFactor}, ${yFactor}) translate(${(ev.clientX) * (xFactor)}px, ${(ev.clientY) * (yFactor)}px)`
+      }),
+      shareReplay(1),
+    )
+
+    this.subscriptions.push(
+      this.transformString$.subscribe()
+    )
+
+    const width$ = this.mousedown$.pipe(
+      switchMap(ev => this.mousemove$.pipe(
+        map(moveEv => Math.abs(moveEv.clientX - ev.clientX)),
+        startWith(0),
+      )),
+      shareReplay(1),
+    )
+
+    this.widthString$ = width$.pipe(
+      map(width => `${width}px`),
+      shareReplay(1),
+    )
+
+    this.subscriptions.push(
+      this.widthString$.subscribe()
+    )
+
+    const height$ = this.mousedown$.pipe(
+      switchMap(ev => this.mousemove$.pipe(
+        map(moveEv => Math.abs(moveEv.clientY - ev.clientY)),
+        startWith(0),
+      )),
+      shareReplay(1),
+    )
+
+    this.heightString$ = height$.pipe(
+      map(height => `${height}px`),
+      shareReplay(1),
+    )
+
+    this.subscriptions.push(
+      this.heightString$.subscribe()
+    )
+
+    this.subscriptions.push(
+      combineLatest([
+        this.mousedown$,
+        this.flipRect$,
+        width$,
+        height$,
+      ]).pipe(
+        switchMap(arg => this.mouseup$.pipe(
+          mapTo(arg)
+        ))
+      ).subscribe(([ startEv, flipRect, width, height ]) => {
+        const startX = startEv.clientX
+        const startY = startEv.clientY
+        if (!handleScreenshotPromise) {
+          console.warn(`HANDLE_SCREENSHOT_PROMISE not provided`)
+          return
+        }
+
+        if (width < 5 || height < 5) {
+          snackbar.open('width and height needs to be a bit larger', null, {
+            duration: 1000
+          })
+          return
+        }
+
+        this.captureScreenshot({
+          x: flipRect.x ? startX - width : startX,
+          y: flipRect.y ? startY - height : startY,
+          width,
+          height
+        })
+      })
+    )
+  }
+
+  captureScreenshot(param?){
+
+    this.handleScreenshotPromise(param)
+      .then(({ revoke, url }) => {
+        this.dialog.open(
+          this.previewTmpl,
+          {
+            data: {
+              url,
+              download: `${getDateString()}_${getUuid()}.png`
+            }
+          }
+        ).afterClosed().subscribe(
+          reason => {
+            /**
+             * if user clicks outside, or clicks cancel, emit destroy signal
+             */
+            if (!reason || reason === 'cancel') {
+              this.destroy.emit()
+            }
+            revoke()
+          }
+        )
+      })
+      .catch(e => {
+        this.snackbar.open(e, 'Dismiss')
+      })
+  }
+
+  ngOnDestroy(){
+    while(this.subscriptions.length > 0) this.subscriptions.pop().unsubscribe()
+  }
+
+  @HostListener('mousedown', ['$event'])
+  mousedown(ev: MouseEvent){
+    this.mousedown$.next(ev)
+  }
+
+  @HostListener('mouseup', ['$event'])
+  mouseup(ev: MouseEvent){
+    this.mouseup$.next(ev)
+  }
+
+  @HostListener('mousemove', ['$event'])
+  mousemove(ev: MouseEvent){
+    this.mousemove$.next(ev)
+  }
+
+  @Output()
+  destroy = new EventEmitter()
+
+  @ViewChild('previewTmpl', { read: TemplateRef })
+  private previewTmpl: TemplateRef<any>
+}
\ No newline at end of file
diff --git a/src/ui/screenshot/screenshotCmp/screenshot.style.css b/src/ui/screenshot/screenshotCmp/screenshot.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..052e938e4b62e7c5eb2ab8a1fac99dca19be16b8
--- /dev/null
+++ b/src/ui/screenshot/screenshotCmp/screenshot.style.css
@@ -0,0 +1,30 @@
+:host
+{
+  display: block;
+  width:100%;
+}
+
+.cover
+{
+  background-color: rgba(0,0,0, 0.5);
+}
+
+.box
+{
+  /* background: none; */
+  box-sizing: content-box;
+  width: 0;
+  height: 0;
+  transform-origin: top left;
+}
+
+.inner-box
+{
+  border: 1px white solid;
+}
+
+img
+{
+  max-width: 50vw;
+  min-width: 20vw;
+}
\ No newline at end of file
diff --git a/src/ui/screenshot/screenshotCmp/screenshot.template.html b/src/ui/screenshot/screenshotCmp/screenshot.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..ad7143b67c6c49b75f8341450b70633e997149eb
--- /dev/null
+++ b/src/ui/screenshot/screenshotCmp/screenshot.template.html
@@ -0,0 +1,74 @@
+
+<ng-template #placeholderTmpl>
+
+  <div class="d-flex align-items-center justify-content-center w-100 h-100 cover">
+    <span class="iv-custom-comp text">
+      <h2 class="mat-h2 text-center">
+        <span>
+          Drag a box to take a screenshot or
+        </span>
+
+        <button mat-stroked-button iav-stop="mousedown mouseup"
+          (click)="captureScreenshot()">
+          <i class="fas fa-camera"></i>
+          <span class="ml-1">
+            capture whole screen
+          </span>
+        </button>
+      </h2>
+
+      <h3 class="mat-h3 text-center">
+        <span>
+          cancel with Esc or
+        </span>
+        <button iav-stop="mousedown mouseup"
+          (click)="destroy.emit()"
+          mat-button>
+          click here
+        </button>
+      </h3>
+    </span>
+  </div>
+
+</ng-template>
+
+<ng-template [ngIf]="isDragging$ | async" [ngIfElse]="placeholderTmpl">
+  <div [style.border]="borderStyle"
+    [style.transform]="(transformString$ | async)"
+    [style.width]="widthString$ | async"
+    [style.height]="heightString$ | async"
+    class="box">
+    <div class="inner-box h-100 w-100">
+    </div>
+  </div>
+</ng-template>
+
+
+<ng-template #previewTmpl let-data>
+  
+  <mat-dialog-content class="d-flex justify-content-center">
+    <img [src]="data.url | safeResource">
+  </mat-dialog-content>
+
+  <mat-dialog-actions align="end">
+    <a [href]="data.url | safeResource"
+      [download]="data.download"
+      mat-raised-button color="primary">
+      <i class="fas fa-save"></i>
+      <span class="ml-1">Save</span>
+    </a>
+
+    <button mat-stroked-button
+      color="default"
+      mat-dialog-close="try again">
+      <i class="fas fa-camera"></i>
+      <span class="ml-1">Try again</span>
+    </button>
+
+    <button mat-button
+      color="default"
+      mat-dialog-close="cancel">
+      Cancel
+    </button>
+  </mat-dialog-actions>
+</ng-template>
\ No newline at end of file
diff --git a/src/ui/screenshot/screenshotSwitch.directive.ts b/src/ui/screenshot/screenshotSwitch.directive.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e80be541b86313b80cd4c13e15eb7e5d31164dc8
--- /dev/null
+++ b/src/ui/screenshot/screenshotSwitch.directive.ts
@@ -0,0 +1,67 @@
+import { Overlay, OverlayRef } from "@angular/cdk/overlay";
+import { ComponentPortal } from "@angular/cdk/portal";
+import { ComponentRef, Directive, HostListener } from "@angular/core";
+import { take } from "rxjs/operators";
+import { ScreenshotCmp } from "./screenshotCmp/screenshot.component";
+
+@Directive({
+  selector: '[screenshot-switch]'
+})
+
+export class ScreenshotSwitch{
+  public takingScreenshot = false
+
+  private overlayRef: OverlayRef
+  private cmpRef: ComponentRef<ScreenshotCmp>
+
+  @HostListener('window:keydown', ['$event'])
+  keyListener(ev: KeyboardEvent){
+    if (ev.key === 'Escape') {
+      if (this.overlayRef) this.dispose()
+    }
+  }
+
+  @HostListener('click')
+  onClick(){
+    /**
+     * button should act as a toggle?
+     */
+    
+    this.overlayRef = this.overlay.create({
+      hasBackdrop: false,
+      positionStrategy: this.overlay.position().global().centerVertically(),
+      panelClass: ['w-100', 'h-100'],
+    })
+
+    this.cmpRef = this.overlayRef.attach(
+      new ComponentPortal(ScreenshotCmp)
+    )
+    
+    this.cmpRef.instance.destroy.pipe(
+      take(1)
+    ).subscribe(
+      () => {
+        this.dispose()
+      }
+    )
+    
+    
+  }
+
+  dispose(){
+    this.cmpRef = null
+    if (this.overlayRef) this.overlayRef.dispose()
+    this.overlayRef = null
+  }
+
+  reset(){
+    this.takingScreenshot = true
+  }
+
+  clear(){
+    this.takingScreenshot = false
+  }
+
+  constructor(private overlay: Overlay){}
+
+}
\ No newline at end of file
diff --git a/src/ui/screenshot/util.ts b/src/ui/screenshot/util.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5a84dc7f1b76e9a420c3d311d1b7dfdd34e42439
--- /dev/null
+++ b/src/ui/screenshot/util.ts
@@ -0,0 +1,14 @@
+import { InjectionToken } from "@angular/core"
+
+interface IScrnShot{
+  x: number
+  y: number
+  width: number
+  height: number
+}
+
+/**
+ * if param is not provided, screenshot entire screen
+ */
+export type TypeHandleScrnShotPromise = (param?: IScrnShot) => Promise<{ revoke: Function, url: string }>
+export const HANDLE_SCREENSHOT_PROMISE = new InjectionToken('HANDLE_SCREENSHOT_PROMISE')
\ No newline at end of file
diff --git a/src/ui/takeScreenshot/takeScreenshot.component.ts b/src/ui/takeScreenshot/takeScreenshot.component.ts
deleted file mode 100644
index b10f94a06cee887ece6d331b661ec137af58a15d..0000000000000000000000000000000000000000
--- a/src/ui/takeScreenshot/takeScreenshot.component.ts
+++ /dev/null
@@ -1,285 +0,0 @@
-import {DOCUMENT} from "@angular/common";
-import {
-  ChangeDetectorRef,
-  Component,
-  ElementRef, EventEmitter,
-  HostListener,
-  Inject, OnDestroy,
-  OnInit, Output,
-  Renderer2,
-  TemplateRef,
-  ViewChild,
-} from "@angular/core";
-import {MatDialog, MatDialogRef} from "@angular/material/dialog";
-import html2canvas from "html2canvas";
-
-@Component({
-  selector: 'take-screenshot',
-  templateUrl: './takeScreenshot.template.html',
-  styleUrls: ['./takeScreenshot.style.css'],
-})
-
-export class TakeScreenshotComponent implements OnInit, OnDestroy {
-
-  ngOnDestroy(): void {
-    if (this.resettingScreenshotTaking) this.resetScreenshot.emit(true)
-    this.resettingScreenshotTaking = false
-  }
-
-    @ViewChild('screenshotPreviewCard', {read: ElementRef}) public screenshotPreviewCard: ElementRef
-    @ViewChild('previewImageDialog', {read: TemplateRef}) public previewImageDialogTemplateRef: TemplateRef<any>
-
-    @Output() screenshotTaking: EventEmitter<boolean> = new EventEmitter<boolean>()
-    @Output() resetScreenshot: EventEmitter<boolean> = new EventEmitter<boolean>()
-
-    private resettingScreenshotTaking: boolean = false
-
-    private dialogRef: MatDialogRef<any>
-
-    public takingScreenshot: boolean = false
-    public previewingScreenshot: boolean = false
-    public loadingScreenshot: boolean = false
-
-    public screenshotName: string = `screenshot.png`
-    private croppedCanvas = null
-
-    public mouseIsDown = false
-    public isDragging = false
-
-    // Used to calculate where to start showing the dragging area
-    private startX: number = 0
-    private startY: number = 0
-    private endX: number = 0
-    private endY: number = 0
-
-    public borderWidth: string = ''
-    // The box that contains the border and all required numbers.
-    public boxTop: number = 0
-    public boxLeft: number = 0
-    public boxEndWidth: number = 0
-    public boxEndHeight: number = 0
-
-    private windowHeight: number = 0
-    private windowWidth: number = 0
-
-    private screenshotStartX: number = 0
-    private screenshotStartY: number = 0
-
-    public imageUrl: string
-
-    constructor(
-        private renderer: Renderer2,
-        @Inject(DOCUMENT) private document: any,
-        private matDialog: MatDialog,
-        private cdr: ChangeDetectorRef,
-    ) {}
-
-    public ngOnInit(): void {
-      this.windowWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth
-      this.windowHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight
-
-      this.startScreenshot()
-
-    }
-
-    @HostListener('window:resize', ['$event'])
-    public onResize() {
-      this.windowWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth
-      this.windowHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight
-    }
-
-    @HostListener('window:keyup', ['$event'])
-    public keyEvent(event: KeyboardEvent) {
-      if (this.takingScreenshot && event.key === 'Escape') {
-        this.cancelTakingScreenshot()
-      }
-    }
-
-    public startScreenshot() {
-      this.previewingScreenshot = false
-      this.croppedCanvas = null
-      this.loadingScreenshot = false
-      this.takingScreenshot = true
-    }
-
-    public move(e: MouseEvent) {
-      if (this.mouseIsDown) {
-        this.isDragging = true
-
-        this.endY = e.clientY
-        this.endX = e.clientX
-
-        if (this.endX >= this.startX && this.endY >= this.startY) {
-          // III quadrant
-          this.borderWidth = this.startY + 'px '
-                    + (this.windowWidth - this.endX) + 'px '
-                    + (this.windowHeight - this.endY) + 'px '
-                    + this.startX + 'px'
-          this.boxTop = this.startY
-          this.boxLeft = this.startX
-          this.boxEndWidth = this.endX - this.startX
-          this.boxEndHeight = this.endY - this.startY
-
-          this.screenshotStartX = this.startX
-          this.screenshotStartY = this.startY
-
-        } else if (this.endX <= this.startX && this.endY >= this.startY) {
-          // IV quadrant
-
-          this.borderWidth = this.startY + 'px '
-                    + (this.windowWidth - this.startX) + 'px '
-                    + (this.windowHeight - this.endY) + 'px '
-                    + this.endX + 'px'
-
-          this.boxLeft = this.endX
-          this.boxTop = this.startY
-          this.boxEndWidth = this.startX - this.endX
-          this.boxEndHeight = this.endY - this.startY
-
-          this.screenshotStartX = this.endX
-          this.screenshotStartY = this.startY
-
-        } else if (this.endX >= this.startX && this.endY <= this.startY) {
-
-          // II quadrant
-
-          this.borderWidth = this.endY + 'px '
-                    + (this.windowWidth - this.endX) + 'px '
-                    + (this.windowHeight - this.startY) + 'px '
-                    + this.startX + 'px'
-
-          this.boxLeft = this.startX
-          this.boxTop = this.endY
-          this.boxEndWidth = this.endX - this.startX
-          this.boxEndHeight = this.startY - this.endY
-
-          this.screenshotStartX = this.startX
-          this.screenshotStartY = this.endY
-
-        } else if (this.endX <= this.startX && this.endY <= this.startY) {
-          // I quadrant
-
-          this.boxLeft = this.endX
-          this.boxTop = this.endY
-          this.boxEndWidth = this.startX - this.endX
-          this.boxEndHeight = this.startY - this.endY
-
-          this.borderWidth = this.endY + 'px '
-                    + (this.windowWidth - this.startX) + 'px '
-                    + (this.windowHeight - this.startY) + 'px '
-                    + this.endX + 'px'
-
-          this.screenshotStartX = this.endX
-          this.screenshotStartY = this.endY
-
-        } else {
-          this.isDragging = false
-        }
-
-      }
-    }
-
-    public mouseDown(event: MouseEvent) {
-      this.borderWidth = this.windowWidth + 'px ' + this.windowHeight + 'px'
-
-      this.startX = event.clientX
-      this.startY = event.clientY
-
-      this.mouseIsDown = true
-    }
-
-    public mouseUp(_event: MouseEvent) {
-      (window as any).viewer.display.update()
-      this.borderWidth = '0'
-
-      this.isDragging = false
-      this.mouseIsDown = false
-
-      this.takingScreenshot = false
-
-      if (this.boxEndWidth * window.devicePixelRatio <= 1 && this.boxEndHeight * window.devicePixelRatio <= 1) {
-        this.cancelTakingScreenshot()
-      } else {
-        this.loadScreenshot()
-      }
-
-    }
-
-    public loadScreenshot() {
-
-      this.loadingScreenshot = true
-      this.dialogRef = this.matDialog.open(this.previewImageDialogTemplateRef, {
-        autoFocus: false,
-      })
-      this.dialogRef.afterClosed().toPromise()
-        .then(result => {
-          switch (result) {
-          case 'again': {
-            this.restartScreenshot()
-            this.startScreenshot()
-            this.cdr.markForCheck()
-            break
-          }
-          case 'cancel': {
-            this.cancelTakingScreenshot()
-            break
-          }
-          default: this.cancelTakingScreenshot()
-          }
-        })
-
-      html2canvas(this.document.querySelector('#neuroglancer-container canvas')).then(canvas => {
-        this.croppedCanvas = null
-        this.croppedCanvas = this.renderer.createElement('canvas')
-
-        this.croppedCanvas.width = this.boxEndWidth * window.devicePixelRatio
-        this.croppedCanvas.height = this.boxEndHeight * window.devicePixelRatio
-
-        this.croppedCanvas.getContext('2d')
-          .drawImage(canvas,
-            this.screenshotStartX * window.devicePixelRatio, this.screenshotStartY * window.devicePixelRatio,
-            this.boxEndWidth * window.devicePixelRatio, this.boxEndHeight * window.devicePixelRatio,
-            0, 0,
-            this.boxEndWidth * window.devicePixelRatio, this.boxEndHeight * window.devicePixelRatio)
-      }).then(() => {
-
-        const d = new Date()
-        const n = `${d.getFullYear()}_${d.getMonth() + 1}_${d.getDate()}_${d.getHours()}_${d.getMinutes()}_${d.getSeconds()}`
-        this.screenshotName = `${n}_IAV.png`
-
-        this.loadingScreenshot = false
-        this.imageUrl = this.croppedCanvas.toDataURL('image/png')
-        this.previewingScreenshot = true
-        this.clearStateAfterScreenshot()
-
-        this.cdr.markForCheck()
-      })
-    }
-
-    public restartScreenshot() {
-      this.resettingScreenshotTaking = true
-      this.resetScreenshot.emit(false)
-    }
-
-    public cancelTakingScreenshot() {
-      this.screenshotTaking.emit(false)
-    }
-
-    public clearStateAfterScreenshot() {
-      this.mouseIsDown = false
-      this.isDragging = false
-      this.startX = 0
-      this.startY = 0
-      this.endX = 0
-      this.endY = 0
-      this.borderWidth = ''
-      this.boxTop = 0
-      this.boxLeft = 0
-      this.boxEndWidth = 0
-      this.boxEndHeight = 0
-      this.windowHeight = 0
-      this.windowWidth = 0
-      this.screenshotStartX = 0
-      this.screenshotStartY = 0
-    }
-}
diff --git a/src/ui/takeScreenshot/takeScreenshot.style.css b/src/ui/takeScreenshot/takeScreenshot.style.css
deleted file mode 100644
index f104726c32b3c6a08edb1bc04f005a7fa25ccb4a..0000000000000000000000000000000000000000
--- a/src/ui/takeScreenshot/takeScreenshot.style.css
+++ /dev/null
@@ -1,37 +0,0 @@
-.overlay,
-.tooltip,
-.borderedBox {
-    user-select: none;
-}
-
-.overlay {
-    background-color: rgba(0, 0, 0, 0.5);
-}
-
-.overlay.highlighting {
-    background: none;
-    border-color: rgba(0, 0, 0, 0.5);
-    border-style: solid;
-}
-
-.screenshotContainer {
-    clear: both;
-    background-repeat: no-repeat;
-    background-size: cover;
-
-    transition: opacity ease-in-out 200ms;
-}
-
-.smallSizeWindow {
-    height: 40px;
-}
-
-.cancelTimesPosition {
-    top: 5px;
-    right: -10px;
-}
-
-.screenshotPreviewCard {
-    z-index: 10520 !important;
-    background:none;
-}
\ No newline at end of file
diff --git a/src/ui/takeScreenshot/takeScreenshot.template.html b/src/ui/takeScreenshot/takeScreenshot.template.html
deleted file mode 100644
index edbdcfa45775b870299274dc5f205b9902116a97..0000000000000000000000000000000000000000
--- a/src/ui/takeScreenshot/takeScreenshot.template.html
+++ /dev/null
@@ -1,62 +0,0 @@
-<div class="screenshotContainer overflow-hidden w-50 h-50"
-    (mousemove)="move($event)"
-    (mousedown)="mouseDown($event)"
-    (mouseup)="mouseUp($event)"
-    [ngClass]="{'pe-none o-0':!takingScreenshot, 'o-1': takingScreenshot}"
-    [ngStyle]="{'cursor':takingScreenshot? 'crosshair' : 'auto'}">
-
-    <div class="overlay position-fixed fixed-top w-100 h-100 d-flex align-items-center justify-content-center"
-        [ngClass]="{ 'highlighting' : mouseIsDown }"
-        [ngStyle]="{ borderWidth: borderWidth }">
-
-        <!-- instruction text -->
-        <mat-card *ngIf="!isDragging" class="screenshotPreviewCard pe-none">
-            <mat-card-title>
-                Drag a box to take a screenshot
-            </mat-card-title>
-            <mat-card-subtitle class="text-muted d-flex justify-content-center">
-                cancel with Esc
-            </mat-card-subtitle>
-        </mat-card>
-    </div>
-    <div class="position-absolute border border-light"
-        *ngIf="isDragging"
-        [ngStyle]="{ left: boxLeft + 'px', top: boxTop + 'px', width: boxEndWidth + 'px', height: boxEndHeight + 'px' }">
-    </div>
-</div>
-
-<ng-template #previewImageDialog>
-    <mat-dialog-content>
-
-        <div class="d-flex w-100 h-100 justify-content-center align-items-center" *ngIf="loadingScreenshot">
-            <span class="text-nowrap">Generating screenshot </span> <i class="fas fa-spinner fa-pulse ml-1"></i>
-        </div>
-        <ng-template [ngIf]="!loadingScreenshot">
-            <img [src]="imageUrl" class="w-100 h-100">
-        </ng-template>
-    </mat-dialog-content>
-    <mat-dialog-actions align="end">
-
-        <a *ngIf="imageUrl"
-            [href]="imageUrl"
-            [download]="screenshotName">
-
-            <button mat-raised-button
-                color="primary"
-                class="mr-2">
-                <i class="fas fa-save"></i> Save
-            </button>
-        </a>
-        <button mat-stroked-button
-            color="default"
-            class="mr-2"
-            mat-dialog-close="again">
-            <i class="fas fa-camera"></i> Try again
-        </button>
-        <button mat-button
-            color="default"
-            mat-dialog-close="cancel">
-            Cancel
-        </button>
-    </mat-dialog-actions>
-</ng-template>
\ No newline at end of file
diff --git a/src/ui/templateParcellationCitations/templateParcellationCitations.component.ts b/src/ui/templateParcellationCitations/templateParcellationCitations.component.ts
deleted file mode 100644
index c97715d798fbf1345ee5832cd890d2ebcb0ef477..0000000000000000000000000000000000000000
--- a/src/ui/templateParcellationCitations/templateParcellationCitations.component.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { Component } from "@angular/core";
-import { select, Store } from "@ngrx/store";
-import { Observable } from "rxjs";
-import { map, switchMap } from "rxjs/operators";
-import { safeFilter, IavRootStoreInterface } from "../../services/stateStore.service";
-
-@Component({
-  selector : 'template-parcellation-citation-container',
-  templateUrl : './templateParcellationCitations.template.html',
-  styleUrls : [
-    './templateParcellationCitations.style.css',
-  ],
-})
-
-export class TemplateParcellationCitationsContainer {
-  public selectedTemplate$: Observable<any>
-  public selectedParcellation$: Observable<any>
-
-  constructor(private store: Store<IavRootStoreInterface>) {
-    this.selectedTemplate$ = this.store.pipe(
-      select('viewerState'),
-      safeFilter('templateSelected'),
-      map(state => state.templateSelected),
-    )
-
-    this.selectedParcellation$ = this.selectedTemplate$.pipe(
-      switchMap(() => this.store.pipe(
-        select('viewerState'),
-        safeFilter('parcellationSelected'),
-        map(state => state.parcellationSelected),
-      )),
-    )
-  }
-}
diff --git a/src/ui/templateParcellationCitations/templateParcellationCitations.template.html b/src/ui/templateParcellationCitations/templateParcellationCitations.template.html
deleted file mode 100644
index 48b54d534f9efe9b53f055ccbae85f689afe67d9..0000000000000000000000000000000000000000
--- a/src/ui/templateParcellationCitations/templateParcellationCitations.template.html
+++ /dev/null
@@ -1,11 +0,0 @@
-<citations-component 
-  *ngIf = "selectedTemplate$ | async"
-  [properties] = "(selectedTemplate$ | async).properties"
-  citationContainer>
-
-</citations-component>
-<citations-component
-  *ngIf = "selectedParcellation$ | async"
-  [properties] = "(selectedParcellation$ | async).properties"
-  citationContainer>
-</citations-component>
\ No newline at end of file
diff --git a/src/ui/topMenu/index.ts b/src/ui/topMenu/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/ui/topMenu/module.ts b/src/ui/topMenu/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0d5b1aabd1cfca8d3a9bcf80b4bbd63687f2f1bc
--- /dev/null
+++ b/src/ui/topMenu/module.ts
@@ -0,0 +1,45 @@
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { DatabrowserModule } from "src/atlasComponents/databrowserModule";
+import { AuthModule } from "src/auth";
+import { ComponentsModule } from "src/components";
+import { FabSpeedDialModule } from "src/components/fabSpeedDial";
+import { PluginModule } from "src/plugin";
+import { UtilModule } from "src/util";
+import { ConfigModule } from "../config/module";
+import { CookieModule } from "../cookieAgreement/module";
+import { HelpModule } from "../help/module";
+import { KgTosModule } from "../kgtos/module";
+import { ScreenshotModule } from "../screenshot";
+import { AngularMaterialModule } from "../sharedModules/angularMaterial.module";
+import { TopMenuCmp } from "./topMenuCmp/topMenu.components";
+import { UserAnnotationsModule } from "src/atlasComponents/userAnnotations";
+import { QuickTourModule } from "src/ui/quickTour/module";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    UtilModule,
+    AngularMaterialModule,
+    DatabrowserModule,
+    FabSpeedDialModule,
+    ComponentsModule,
+    CookieModule,
+    KgTosModule,
+    ConfigModule,
+    HelpModule,
+    PluginModule,
+    AuthModule,
+    ScreenshotModule,
+    UserAnnotationsModule,
+    QuickTourModule,
+  ],
+  declarations: [
+    TopMenuCmp
+  ],
+  exports: [
+    TopMenuCmp
+  ]
+})
+
+export class TopMenuModule{}
diff --git a/src/ui/signinBanner/signinBanner.components.ts b/src/ui/topMenu/topMenuCmp/topMenu.components.ts
similarity index 75%
rename from src/ui/signinBanner/signinBanner.components.ts
rename to src/ui/topMenu/topMenuCmp/topMenu.components.ts
index 9c823d35fbd4ee77f8ff656b43f2b87e16ff2816..92f65b7268a2760a01f2639b3ecb721edd1f046e 100644
--- a/src/ui/signinBanner/signinBanner.components.ts
+++ b/src/ui/topMenu/topMenuCmp/topMenu.components.ts
@@ -1,11 +1,8 @@
 import {
   ChangeDetectionStrategy,
-  ChangeDetectorRef,
   Component,
-  ElementRef,
   Input,
   TemplateRef,
-  ViewChild
 } from "@angular/core";
 import { select, Store } from "@ngrx/store";
 import { Observable } from "rxjs";
@@ -14,23 +11,23 @@ import { AuthService } from "src/auth";
 import { IavRootStoreInterface, IDataEntry } from "src/services/stateStore.service";
 import { MatDialog, MatDialogConfig, MatDialogRef } from "@angular/material/dialog";
 import { MatBottomSheet } from "@angular/material/bottom-sheet";
-import { CONST } from 'common/constants'
+import { CONST, QUICKTOUR_DESC } from 'common/constants'
+import { IQuickTourData } from "src/ui/quickTour/constrants";
 
 @Component({
-  selector: 'signin-banner',
-  templateUrl: './signinBanner.template.html',
+  selector: 'top-menu-cmp',
+  templateUrl: './topMenu.template.html',
   styleUrls: [
-    './signinBanner.style.css',
-    '../btnShadow.style.css',
+    './topMenu.style.css',
   ],
   changeDetection: ChangeDetectionStrategy.OnPush,
 })
 
-export class SigninBanner {
+export class TopMenuCmp {
 
   public PINNED_DATASETS_BADGE_DESC = CONST.PINNED_DATASETS_BADGE_DESC
 
-  public matBtnStyle = ''
+  public matBtnStyle = 'mat-icon-button'
   public matBtnColor = 'primary'
 
   private _ismobile = false
@@ -45,9 +42,7 @@ export class SigninBanner {
   }
 
   @Input() public darktheme: boolean
-  @Input() public parcellationIsSelected: boolean
-
-  @ViewChild('takeScreenshotElement', {read: ElementRef}) takeScreenshotElement: ElementRef
+  @Input() public viewerLoaded: boolean
 
   public user$: Observable<any>
   public userBtnTooltip$: Observable<string>
@@ -55,14 +50,18 @@ export class SigninBanner {
 
   public pluginTooltipText: string = `Plugins and Tools`
   public screenshotTooltipText: string = 'Take screenshot'
-  public takingScreenshot: boolean = false
+  public annotateTooltipText: string = 'Start annotating'
+
+  public quickTourData: IQuickTourData = {
+    description: QUICKTOUR_DESC.TOP_MENU,
+    order: 8,
+  }
 
   constructor(
     private store$: Store<IavRootStoreInterface>,
     private authService: AuthService,
     private dialog: MatDialog,
     public bottomSheet: MatBottomSheet,
-    private changeDetectionRef: ChangeDetectorRef,
   ) {
     this.user$ = this.authService.user$
 
@@ -79,6 +78,9 @@ export class SigninBanner {
   }
 
   private dialogRef: MatDialogRef<any>
+  public helperOnePagerConfig = {
+    panelClass: ['col-lg-10']
+  }
 
   public openTmplWithDialog(tmpl: TemplateRef<any>, overwriteConfig?: Partial<MatDialogConfig>) {
     this.dialogRef && this.dialogRef.close()
@@ -92,17 +94,6 @@ export class SigninBanner {
     }
   }
 
-  resetScreenshotTaking() {
-    this.takingScreenshot = true
-    //ToDo find out better way to detect changes
-    setTimeout(() => this.changeDetectionRef.detectChanges())
-  }
-
-  disableScreenshotTaking() {
-    this.takingScreenshot = false
-    this.changeDetectionRef.detectChanges()
-  }
-
   private keyListenerConfigBase = {
     type: 'keydown',
     stop: true,
diff --git a/src/ui/signinBanner/signinBanner.style.css b/src/ui/topMenu/topMenuCmp/topMenu.style.css
similarity index 79%
rename from src/ui/signinBanner/signinBanner.style.css
rename to src/ui/topMenu/topMenuCmp/topMenu.style.css
index 787a6e42e83b544a36a33d1f634480724c5baeda..d1021184f2955c2dd4721ee4eb86731ff32f57ee 100644
--- a/src/ui/signinBanner/signinBanner.style.css
+++ b/src/ui/topMenu/topMenuCmp/topMenu.style.css
@@ -10,11 +10,6 @@
   pointer-events: all;
 }
 
-take-screenshot
-{
-  z-index: 1509;
-}
-
 .btnWrapper
 {
   margin: 0.5rem 0;
diff --git a/src/ui/signinBanner/signinBanner.template.html b/src/ui/topMenu/topMenuCmp/topMenu.template.html
similarity index 89%
rename from src/ui/signinBanner/signinBanner.template.html
rename to src/ui/topMenu/topMenuCmp/topMenu.template.html
index ae9fe7663c001f81f708fc4f296fdc5a691a42d4..cb253e7ecf66bd29d1cb972f7d3f491b1e220f3d 100644
--- a/src/ui/signinBanner/signinBanner.template.html
+++ b/src/ui/topMenu/topMenuCmp/topMenu.template.html
@@ -1,6 +1,7 @@
 <ng-template [ngIf]="ismobile" [ngIfElse]="fullTmpl">
   <div iav-fab-speed-dial-container
-    #fab="iavFabSpeedDialContainer">
+    #fab="iavFabSpeedDialContainer"
+    class="d-flex flex-column align-items-center">
     <button mat-fab
       iav-fab-speed-dial-trigger
       [color]="fab.isOpen ? 'basic' : 'accent'">
@@ -41,8 +42,12 @@
 <ng-template #fullTmpl>
 
   <div class="d-flex flex-row-reverse"
+    quick-tour
+    [quick-tour-description]="quickTourData.description"
+    [quick-tour-order]="quickTourData.order"
+    quick-tour-severity="low"
     [iav-key-listener]="keyListenerConfig"
-    (iav-key-event)="openTmplWithDialog(helperOnePager)">
+    (iav-key-event)="openTmplWithDialog(helperOnePager, helperOnePagerConfig)">
 
     <!-- signin -->
     <ng-container *ngTemplateOutlet="signinBtnTmpl">
@@ -122,7 +127,7 @@
 <ng-template #helpBtnTmpl>
 
   <div class="btnWrapper"
-    (click)="openTmplWithDialog(helperOnePager)"
+    (click)="openTmplWithDialog(helperOnePager, helperOnePagerConfig)"
     matTooltip="Quickstart">
     <iav-dynamic-mat-button
       [iav-dynamic-mat-button-style]="matBtnStyle"
@@ -138,12 +143,25 @@
 <mat-menu #pluginDropdownMenu
   [aria-label]="'Tools and plugins menu'">
   <button mat-menu-item
-    [disabled]="!parcellationIsSelected"
-    (click)="takingScreenshot = true;"
+    [disabled]="!viewerLoaded"
+    screenshot-switch
     [matTooltip]="screenshotTooltipText">
     <mat-icon fontSet="fas" fontIcon="fa-camera">
     </mat-icon>
-    Screenshot
+    <span>
+      Screenshot
+    </span>
+  </button>
+  <button mat-menu-item
+    [disabled]="!viewerLoaded"
+    annotation-switch
+    annotation-switch-mode="on"
+    [matTooltip]="annotateTooltipText">
+    <mat-icon fontSet="fas" fontIcon="fa-pencil-ruler">
+    </mat-icon>
+    <span>
+      Annotation mode
+    </span>
   </button>
   <plugin-banner></plugin-banner>
 </mat-menu>
@@ -177,8 +195,8 @@
   <mat-dialog-content>
     <mat-tab-group>
       <mat-tab label="About">
-        <help-component>
-        </help-component>
+        <iav-about>
+        </iav-about>
       </mat-tab>
       <mat-tab label="Privacy Policy">
         <!-- TODO make tab container scrollable -->
@@ -254,11 +272,3 @@
     </mat-list-item>
   </mat-list>
 </ng-template>
-
-<take-screenshot
-  #takeScreenshotElement
-  *ngIf="takingScreenshot"
-  class="position-fixed fixed-top"
-  (screenshotTaking)="disableScreenshotTaking()"
-  (resetScreenshot)="$event? resetScreenshotTaking() : disableScreenshotTaking()">
-</take-screenshot>
diff --git a/src/ui/ui.module.ts b/src/ui/ui.module.ts
index 5697352780b207f09e5d157a482735daae5b1ed6..ff474d2afc77c8adee2ec5174493770321d320c7 100644
--- a/src/ui/ui.module.ts
+++ b/src/ui/ui.module.ts
@@ -1,11 +1,8 @@
-import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from "@angular/core";
+import { NgModule } from "@angular/core";
 import { ComponentsModule } from "src/components/components.module";
 
 import { FormsModule, ReactiveFormsModule } from "@angular/forms";
 import { LayoutModule } from "src/layouts/layout.module";
-import { NehubaContainer } from "./nehubaContainer/nehubaContainer.component";
-import { IMPORT_NEHUBA_INJECT_TOKEN } from "./nehubaContainer/nehubaViewer/nehubaViewer.component";
-import { GetTemplateImageSrcPipe, ImgSrcSetPipe, SplashScreen } from "./nehubaContainer/splashScreen/splashScreen.component";
 
 import { FilterRegionDataEntries } from "src/util/pipes/filterRegionDataEntries.pipe";
 import { GroupDatasetByRegion } from "src/util/pipes/groupDataEntriesByRegion.pipe";
@@ -13,9 +10,6 @@ import { GroupDatasetByRegion } from "src/util/pipes/groupDataEntriesByRegion.pi
 import { GetLayerNameFromDatasets } from "../util/pipes/getLayerNamePipe.pipe";
 import { SortDataEntriesToRegion } from "../util/pipes/sortDataEntriesIntoRegion.pipe";
 import { CitationsContainer } from "./citation/citations.component";
-import { KgEntryViewer } from "./kgEntryViewer/kgentry.component";
-import { SubjectViewer } from "./kgEntryViewer/subjectViewer/subjectViewer.component";
-import { PluginBannerUI } from "./pluginBanner/pluginBanner.component";
 
 import { ScrollingModule } from "@angular/cdk/scrolling"
 import { HttpClientModule } from "@angular/common/http";
@@ -26,66 +20,34 @@ import { GetFileExtension } from "src/util/pipes/getFileExt.pipe";
 import { UtilModule } from "src/util";
 import { DownloadDirective } from "../util/directives/download.directive";
 import { SpatialLandmarksToDataBrowserItemPipe } from "../util/pipes/spatialLandmarksToDatabrowserItem.pipe";
-import { ConfigComponent } from './config/config.component'
-import { CurrentLayout } from "./config/currentLayout/currentLayout.component";
-import { FourPanelLayout } from "./config/layouts/fourPanel/fourPanel.component";
-import { HorizontalOneThree } from "./config/layouts/h13/h13.component";
-import { SinglePanel } from "./config/layouts/single/single.component";
-import { VerticalOneThree } from "./config/layouts/v13/v13.component";
-import { CookieAgreement } from "./cookieAgreement/cookieAgreement.component";
-import { DatabrowserModule } from "./databrowserModule/databrowser.module";
-import { HelpComponent } from "./help/help.component";
-import { KGToS } from "./kgtos/kgtos.component";
+
+
+import { DatabrowserModule } from "../atlasComponents/databrowserModule/databrowser.module";
+
 import { LogoContainer } from "./logoContainer/logoContainer.component";
 import { MobileOverlay } from "./nehubaContainer/mobileOverlay/mobileOverlay.component";
 import { MobileControlNubStylePipe } from "./nehubaContainer/pipes/mobileControlNubStyle.pipe";
-import { StatusCardComponent } from "./nehubaContainer/statusCard/statusCard.component";
-import { SigninBanner } from "./signinBanner/signinBanner.components";
-
-import { TemplateParcellationCitationsContainer } from "./templateParcellationCitations/templateParcellationCitations.component";
-import { FilterNameBySearch } from "./viewerStateController/regionHierachy/filterNameBySearch.pipe";
-
-import { ViewerStateMini } from 'src/ui/viewerStateController/viewerStateCMini/viewerStateMini.component'
 
 import { HumanReadableFileSizePipe } from "src/util/pipes/humanReadableFileSize.pipe";
 import { KgSearchBtnColorPipe } from "src/util/pipes/kgSearchBtnColor.pipe";
 import { PluginBtnFabColorPipe } from "src/util/pipes/pluginBtnFabColor.pipe";
 import { TemplateParcellationHasMoreInfo } from "src/util/pipes/templateParcellationHasMoreInfo.pipe";
-import { MaximmisePanelButton } from "./nehubaContainer/maximisePanelButton/maximisePanelButton.component";
+
 import { ReorderPanelIndexPipe } from "./nehubaContainer/reorderPanelIndex.pipe";
-import { TouchSideClass } from "./nehubaContainer/touchSideClass.directive";
-import { BinSavedRegionsSelectionPipe, SavedRegionsSelectionBtnDisabledPipe } from "./viewerStateController/viewerState.pipes";
 
-import { TakeScreenshotComponent } from "src/ui/takeScreenshot/takeScreenshot.component";
 import { FixedMouseContextualContainerDirective } from "src/util/directives/FixedMouseContextualContainerDirective.directive";
-import { RegionHierarchy } from './viewerStateController/regionHierachy/regionHierarchy.component'
-import { RegionTextSearchAutocomplete } from "./viewerStateController/regionSearch/regionSearch.component";
-
-import { ConnectivityBrowserComponent } from "src/ui/connectivityBrowser/connectivityBrowser.component";
-import { RegionMenuComponent } from 'src/ui/parcellationRegion/regionMenu/regionMenu.component'
-import { RegionListSimpleViewComponent } from "./parcellationRegion/regionListSimpleView/regionListSimpleView.component";
-import { SimpleRegionComponent } from "./parcellationRegion/regionSimple/regionSimple.component";
-import { LandmarkUIComponent } from "./landmarkUI/landmarkUI.component";
-import { NehubaModule } from "./nehubaContainer/nehuba.module";
+
 import { ShareModule } from "src/share";
-import { StateModule } from "src/state";
 import { AuthModule } from "src/auth";
-import { FabSpeedDialModule } from "src/components/fabSpeedDial";
 import { ActionDialog } from "./actionDialog/actionDialog.component";
-import { NehubaViewerTouchDirective } from "./nehubaContainer/nehubaViewerInterface/nehubaViewerTouch.directive";
-import { importNehubaFactory } from "./nehubaContainer/util";
 import { APPEND_SCRIPT_TOKEN, appendScriptFactory } from "src/util/constants";
 import { DOCUMENT } from "@angular/common";
-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";
-import { RegionAccordionTooltipTextPipe } from './util'
-import { HelpOnePager } from "./helpOnePager/helpOnePager.component";
-import { RegionalFeaturesModule } from "./regionalFeatures";
+import { RegionalFeaturesModule } from "../atlasComponents/regionalFeatures";
 import { Landmark2DModule } from "./nehubaContainer/2dLandmarks/module";
-import { ViewerCtrlModule } from "src/viewerCtrl";
+import { HANDLE_SCREENSHOT_PROMISE, TypeHandleScrnShotPromise } from "./screenshot";
+import { ParcellationRegionModule } from "src/atlasComponents/parcellationRegion";
+import { AtlasCmpParcellationModule } from "src/atlasComponents/parcellation";
 
 @NgModule({
   imports : [
@@ -99,52 +61,18 @@ import { ViewerCtrlModule } from "src/viewerCtrl";
     UtilModule,
     ScrollingModule,
     AngularMaterialModule,
-    NehubaModule,
     ShareModule,
-    StateModule,
     AuthModule,
-    FabSpeedDialModule,
     RegionalFeaturesModule,
     Landmark2DModule,
-    ViewerCtrlModule,
+    ParcellationRegionModule,
+    AtlasCmpParcellationModule,
   ],
   declarations : [
-    NehubaContainer,
     
-    SplashScreen,
-    PluginBannerUI,
     CitationsContainer,
-    KgEntryViewer,
-    SubjectViewer,
     LogoContainer,
-    TemplateParcellationCitationsContainer,
     MobileOverlay,
-    HelpComponent,
-    ConfigComponent,
-    SigninBanner,
-    AtlasDropdownSelector,
-    AtlasLayerSelector,
-    AtlasDropdownSelector,
-
-    StatusCardComponent,
-    CookieAgreement,
-    KGToS,
-    FourPanelLayout,
-    HorizontalOneThree,
-    VerticalOneThree,
-    SinglePanel,
-    CurrentLayout,
-    ViewerStateMini,
-    RegionHierarchy,
-    MaximmisePanelButton,
-    RegionTextSearchAutocomplete,
-    TakeScreenshotComponent,
-    RegionMenuComponent,
-    ConnectivityBrowserComponent,
-    SimpleRegionComponent,
-    RegionListSimpleViewComponent,
-    LandmarkUIComponent,
-    HelpOnePager,
 
     ActionDialog,
 
@@ -155,79 +83,96 @@ import { ViewerCtrlModule } from "src/viewerCtrl";
     GetLayerNameFromDatasets,
     SortDataEntriesToRegion,
     SpatialLandmarksToDataBrowserItemPipe,
-    FilterNameBySearch,
     AppendtooltipTextPipe,
     MobileControlNubStylePipe,
-    GetTemplateImageSrcPipe,
-    ImgSrcSetPipe,
     PluginBtnFabColorPipe,
     KgSearchBtnColorPipe,
     GetFileExtension,
-    BinSavedRegionsSelectionPipe,
-    SavedRegionsSelectionBtnDisabledPipe,
 
     TemplateParcellationHasMoreInfo,
     HumanReadableFileSizePipe,
     ReorderPanelIndexPipe,
-    RenderViewOriginDatasetLabelPipe,
-    RegionAccordionTooltipTextPipe,
 
     /* directive */
     DownloadDirective,
-    TouchSideClass,
     FixedMouseContextualContainerDirective,
-    NehubaViewerTouchDirective,
-    RegionDirective
   ],
   providers: [
-    {
-      provide: IMPORT_NEHUBA_INJECT_TOKEN,
-      useFactory: importNehubaFactory,
-      deps: [ APPEND_SCRIPT_TOKEN ]
-    },
     {
       provide: APPEND_SCRIPT_TOKEN,
       useFactory: appendScriptFactory,
       deps: [ DOCUMENT ]
+    },
+    {
+      provide: HANDLE_SCREENSHOT_PROMISE,
+      useValue: ((param) => {
+        const canvas: HTMLCanvasElement = document.querySelector('#neuroglancer-container canvas')
+        if (!canvas) return Promise.reject(`element '#neuroglancer-container canvas' not found`)
+        const _ = (window as any).viewer.display.draw()
+        if (!param) {
+          return new Promise(rs => {
+            canvas.toBlob(blob => {
+              const url = URL.createObjectURL(blob)
+              rs({
+                url,
+                revoke: () => URL.revokeObjectURL(url)
+              })
+            }, 'image/png')
+          })
+        }
+        const { x, y, width, height } = param
+        return new Promise(rs => {
+          const subCanvas = document.createElement('canvas')
+          subCanvas.width = width
+          subCanvas.height = height
+          const context = subCanvas.getContext('2d')
+          context.drawImage(
+            canvas,
+
+            /**
+             * from
+             */
+            x,
+            y,
+            width,
+            height,
+
+            /**
+             * to
+             */
+            0,
+            0,
+            width,
+            height
+          )
+
+          subCanvas.toBlob(blob => {
+            const url = URL.createObjectURL(blob)
+            rs({
+              url,
+              revoke: () => URL.revokeObjectURL(url)
+            })
+          }, 'image/png')
+        })
+      }) as TypeHandleScrnShotPromise
     }
   ],
   entryComponents : [
 
     /* dynamically created components needs to be declared here */
     
-    PluginBannerUI,
     ActionDialog,
   ],
   exports : [
-    SubjectViewer,
-    KgEntryViewer,
     CitationsContainer,
-    PluginBannerUI,
-    NehubaContainer,
+    // NehubaContainer,
     
     LogoContainer,
-    TemplateParcellationCitationsContainer,
     MobileOverlay,
-    HelpComponent,
-    ConfigComponent,
-    SigninBanner,
-    AtlasLayerSelector,
-    RegionDirective,
     
-    CookieAgreement,
-    KGToS,
-    StatusCardComponent,
-    ViewerStateMini,
-    RegionMenuComponent,
+    // StatusCardComponent,
     FixedMouseContextualContainerDirective,
-    LandmarkUIComponent,
-    NehubaViewerTouchDirective,
-    AtlasDropdownSelector,
-    RenderViewOriginDatasetLabelPipe,
-  ],
-  schemas: [
-    CUSTOM_ELEMENTS_SCHEMA,
-  ],
+  ]
 })
 
 export class UIModule {
diff --git a/src/ui/viewerStateController/viewerState.base.ts b/src/ui/viewerStateController/viewerState.base.ts
deleted file mode 100644
index 70a67c226bec656ba46429e5d9cd203605a5fcd9..0000000000000000000000000000000000000000
--- a/src/ui/viewerStateController/viewerState.base.ts
+++ /dev/null
@@ -1,201 +0,0 @@
-import { OnInit, TemplateRef, ViewChild } from "@angular/core";
-import { select, Store } from "@ngrx/store";
-import { Observable, Subscription } from "rxjs";
-import { distinctUntilChanged, filter, shareReplay } from "rxjs/operators";
-import { DialogService } from "src/services/dialogService.service";
-import { RegionSelection } from "src/services/state/userConfigState.store";
-import { IavRootStoreInterface, SELECT_REGIONS, USER_CONFIG_ACTION_TYPES } from "src/services/stateStore.service";
-import { MatSelectChange } from "@angular/material/select";
-import { MatBottomSheet, MatBottomSheetRef } from "@angular/material/bottom-sheet";
-import { viewerStateSelectTemplateWithName } from "src/services/state/viewerState/actions";
-
-const ACTION_TYPES = {
-  SELECT_PARCELLATION_WITH_NAME: 'SELECT_PARCELLATION_WITH_NAME',
-
-}
-
-export class ViewerStateBase implements OnInit {
-
-  @ViewChild('savedRegionBottomSheetTemplate', {read: TemplateRef}) public savedRegionBottomSheetTemplate: TemplateRef<any>
-
-  public focused: boolean = false
-
-  private subscriptions: Subscription[] = []
-
-  public standaloneVolumes$: Observable<any[]>
-
-  public availableTemplates$: Observable<any[]>
-  public availableParcellations$: Observable<any[]>
-
-  public templateSelected$: Observable<any>
-  public parcellationSelected$: Observable<any>
-  public regionsSelected$: Observable<any>
-
-  public savedRegionsSelections$: Observable<any[]>
-
-  private savedRegionBottomSheetRef: MatBottomSheetRef
-
-  constructor(
-    private store$: Store<IavRootStoreInterface>,
-    private dialogService: DialogService,
-    private bottomSheet: MatBottomSheet,
-  ) {
-    const viewerState$ = this.store$.pipe(
-      select('viewerState'),
-      shareReplay(1),
-    )
-
-    this.savedRegionsSelections$ = this.store$.pipe(
-      select('userConfigState'),
-      select('savedRegionsSelection'),
-      shareReplay(1),
-    )
-
-    this.templateSelected$ = viewerState$.pipe(
-      select('templateSelected'),
-      distinctUntilChanged(),
-    )
-
-    this.parcellationSelected$ = viewerState$.pipe(
-      select('parcellationSelected'),
-      distinctUntilChanged(),
-      shareReplay(1),
-    )
-
-    this.regionsSelected$ = viewerState$.pipe(
-      select('regionsSelected'),
-      distinctUntilChanged(),
-      shareReplay(1),
-    )
-
-    this.standaloneVolumes$ = viewerState$.pipe(
-      select('standaloneVolumes'),
-      distinctUntilChanged(),
-      shareReplay(1)
-    )
-
-    this.availableTemplates$ = viewerState$.pipe(
-      select('fetchedTemplates'),
-      distinctUntilChanged()
-    )
-
-    this.availableParcellations$ = this.templateSelected$.pipe(
-      select('parcellations'),
-    )
-
-  }
-
-  public ngOnInit() {
-    this.subscriptions.push(
-      this.savedRegionsSelections$.pipe(
-        filter(srs => srs.length === 0),
-      ).subscribe(() => this.savedRegionBottomSheetRef && this.savedRegionBottomSheetRef.dismiss()),
-    )
-  }
-
-  public handleTemplateChange(event: MatSelectChange) {
-    this.store$.dispatch(
-      viewerStateSelectTemplateWithName({
-        payload: { name: event.value }
-      })
-    )
-  }
-
-  public handleParcellationChange(event: MatSelectChange) {
-    if (!event.value) { return }
-    this.store$.dispatch({
-      type: ACTION_TYPES.SELECT_PARCELLATION_WITH_NAME,
-      payload: {
-        name: event.value,
-      },
-    })
-  }
-
-  public loadSavedRegion(event: MouseEvent, savedRegionsSelection: RegionSelection) {
-    this.store$.dispatch({
-      type: USER_CONFIG_ACTION_TYPES.LOAD_REGIONS_SELECTION,
-      payload: {
-        savedRegionsSelection,
-      },
-    })
-  }
-
-  public editSavedRegion(event: MouseEvent, savedRegionsSelection: RegionSelection) {
-    event.preventDefault()
-    event.stopPropagation()
-    this.dialogService.getUserInput({
-      defaultValue: savedRegionsSelection.name,
-      placeholder: `Enter new name`,
-      title: 'Edit name',
-      iconClass: null,
-    }).then(name => {
-      if (!name) { throw new Error('user cancelled') }
-      this.store$.dispatch({
-        type: USER_CONFIG_ACTION_TYPES.UPDATE_REGIONS_SELECTION,
-        payload: {
-          ...savedRegionsSelection,
-          name,
-        },
-      })
-    }).catch(e => {
-      // TODO catch user cancel
-    })
-  }
-  public removeSavedRegion(event: MouseEvent, savedRegionsSelection: RegionSelection) {
-    event.preventDefault()
-    event.stopPropagation()
-    this.store$.dispatch({
-      type: USER_CONFIG_ACTION_TYPES.DELETE_REGIONS_SELECTION,
-      payload: {
-        ...savedRegionsSelection,
-      },
-    })
-  }
-
-  public trackByFn = ({ name }) => name
-
-  public loadSelection(_event: MouseEvent) {
-    this.focused = true
-
-    this.savedRegionBottomSheetRef = this.bottomSheet.open(this.savedRegionBottomSheetTemplate)
-    this.savedRegionBottomSheetRef.afterDismissed()
-      .subscribe(null, null, () => {
-        this.focused = false
-        this.savedRegionBottomSheetRef = null
-      })
-  }
-
-  public saveSelection(_event: MouseEvent) {
-    this.focused = true
-    this.dialogService.getUserInput({
-      defaultValue: `Saved Region`,
-      placeholder: `Name the selection`,
-      title: 'Save region selection',
-      iconClass: 'far fa-bookmark',
-    })
-      .then(name => {
-        if (!name) { throw new Error('User cancelled') }
-        this.store$.dispatch({
-          type: USER_CONFIG_ACTION_TYPES.SAVE_REGIONS_SELECTION,
-          payload: { name },
-        })
-      })
-      .catch(e => {
-        /**
-         * TODO USER CANCELLED, HANDLE
-         */
-      })
-      .finally(() => this.focused = false)
-  }
-
-  public deselectAllRegions(_event: MouseEvent) {
-    this.store$.dispatch({
-      type: SELECT_REGIONS,
-      selectRegions: [],
-    })
-  }
-
-}
-
-export const VIEWERSTATE_CONTROLLER_ACTION_TYPES = ACTION_TYPES
-
diff --git a/src/ui/viewerStateController/viewerState.pipes.ts b/src/ui/viewerStateController/viewerState.pipes.ts
deleted file mode 100644
index 400ed875be821b17776f5623d11a3e7ebbed2159..0000000000000000000000000000000000000000
--- a/src/ui/viewerStateController/viewerState.pipes.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import { Pipe, PipeTransform } from "@angular/core";
-import { RegionSelection } from "src/services/state/userConfigState.store";
-
-@Pipe({
-  name: 'binSavedRegionsSelectionPipe',
-})
-
-export class BinSavedRegionsSelectionPipe implements PipeTransform {
-  public transform(regionSelections: RegionSelection[]): Array<{parcellationSelected: any, templateSelected: any, regionSelections: RegionSelection[]}> {
-    const returnMap = new Map()
-    for (const regionSelection of regionSelections) {
-      const key = `${regionSelection.templateSelected.name}\n${regionSelection.parcellationSelected.name}`
-      const existing = returnMap.get(key)
-      if (existing) { existing.push(regionSelection) } else { returnMap.set(key, [regionSelection]) }
-    }
-    return Array.from(returnMap)
-      .map(([_unused, regionSelections]) => {
-        const {parcellationSelected = null, templateSelected = null} = regionSelections[0] || {}
-        return {
-          regionSelections,
-          parcellationSelected,
-          templateSelected,
-        }
-      })
-  }
-}
-
-@Pipe({
-  name: 'savedRegionsSelectionBtnDisabledPipe',
-})
-
-export class SavedRegionsSelectionBtnDisabledPipe implements PipeTransform {
-  public transform(regionSelection: RegionSelection, templateSelected: any, parcellationSelected: any): boolean {
-    return regionSelection.parcellationSelected.name !== parcellationSelected.name
-      || regionSelection.templateSelected.name !== templateSelected.name
-  }
-}
diff --git a/src/ui/viewerStateController/viewerStateCMini/viewerStateMini.component.ts b/src/ui/viewerStateController/viewerStateCMini/viewerStateMini.component.ts
deleted file mode 100644
index 66ff71b0c37b93d3ecba2c26980e90b1fa4f22f5..0000000000000000000000000000000000000000
--- a/src/ui/viewerStateController/viewerStateCMini/viewerStateMini.component.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { Component } from "@angular/core";
-import { Store } from "@ngrx/store";
-import { DialogService } from "src/services/dialogService.service";
-
-import { IavRootStoreInterface } from "src/services/stateStore.service";
-import { ViewerStateBase } from '../viewerState.base'
-import {MatBottomSheet} from "@angular/material/bottom-sheet";
-
-@Component({
-  selector: 'viewer-state-mini',
-  templateUrl: './viewerStateMini.template.html',
-  styleUrls: [
-    './viewerStateMini.style.css',
-  ],
-})
-
-export class ViewerStateMini extends ViewerStateBase {
-
-  constructor(
-    store$: Store<IavRootStoreInterface>,
-    dialogService: DialogService,
-    bottomSheet: MatBottomSheet,
-  ) {
-    super(store$, dialogService, bottomSheet)
-  }
-}
diff --git a/src/ui/viewerStateController/viewerStateCMini/viewerStateMini.template.html b/src/ui/viewerStateController/viewerStateCMini/viewerStateMini.template.html
deleted file mode 100644
index 2604321cb2f7ec6f857ab83acaff064338a19284..0000000000000000000000000000000000000000
--- a/src/ui/viewerStateController/viewerStateCMini/viewerStateMini.template.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!-- selected template and parcellation -->
-<div *ngIf="templateSelected$ | async as templateSelected">
-  {{ templateSelected.name }}
-</div>
-<div *ngIf="parcellationSelected$ | async as parcellationSelected">
-  {{ (parcellationSelected.displayName || parcellationSelected.name) | tmpParcNamePipe }}
-</div>
-
-<!-- selected parcellation regions -->
-<ng-container *ngIf="regionsSelected$ | async as regionsSelected">
-  <ng-container *ngIf="regionsSelected.length > 0">
-    <div class="mt-2">
-      {{ regionsSelected.length }} region{{ regionsSelected.length > 1 ? 's' : '' }} selected
-    </div>
-  </ng-container>
-</ng-container>
-
-<ng-container *ngIf="standaloneVolumes$ | async as standaloneVolumes">
-  <div *ngFor="let vol of standaloneVolumes">
-    {{ vol }}
-  </div>
-</ng-container>
\ No newline at end of file
diff --git a/src/util/LinkedList.spec.ts b/src/util/LinkedList.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e81a9a874f9256ececad6424c5c91810cf21d165
--- /dev/null
+++ b/src/util/LinkedList.spec.ts
@@ -0,0 +1,183 @@
+import { DoublyLinkedList, FindInLinkedList } from './LinkedList'
+
+describe('> LinkedList.ts', () => {
+  describe('> DoublyLinkedList', () => {
+    let linkedList: DoublyLinkedList<{}>
+    beforeEach(() => {
+      linkedList = new DoublyLinkedList()
+    })
+
+    it('> expect size === 0', () => {
+      expect(
+        linkedList.size()
+      ).toEqual(0)
+    })
+
+    describe('> insert into empty linked list', () => {
+      const first = {}
+      beforeEach(() => {
+        linkedList.insertAfter(
+          first,
+          () => true
+        )
+      })
+      it('> first === inserted element', () => {
+        expect(
+          linkedList.first.thisObj
+        ).toBe(first)
+      })
+      it('> last === inserted element', () => {
+        expect(
+          linkedList.last.thisObj
+        ).toBe(first)
+      })
+      it('> expect size === 1', () => {
+        expect(
+          linkedList.size()
+        ).toEqual(1)
+      })
+
+      describe('> inserting same item', () => {
+        beforeEach(() => {
+          linkedList.insertAfter(
+            first,
+            () => true
+          )
+        })
+        it('> second insertion will not be counted', () => {
+          expect(
+            linkedList.size()
+          ).toEqual(1)
+        })
+
+        it('> next will be null', () => {
+          expect(
+            linkedList.first.next
+          ).toBeFalsy()
+        })
+      })
+    })
+
+    describe('> insert into occupied linked list', () => {
+      const obj1 = {
+        name: 'obj1'
+      }
+      const obj2 = {
+        name: 'obj2'
+      }
+      const obj3 = {
+        name: 'obj3'
+      }
+      const predicateSpy = jasmine.createSpy('predicate')
+      beforeEach(() => {
+        linkedList.insertAfter(
+          obj1,
+          linkedItem => !linkedItem.next
+        )
+        linkedList.insertAfter(
+          obj2,
+          linkedItem => !linkedItem.next
+        )
+      })
+
+      afterEach(() => {
+        predicateSpy.calls.reset()
+      })
+
+      it('> adding obj calls predicateSpy', () => {
+        predicateSpy.and.returnValue(true)
+        linkedList.insertAfter(
+          obj3,
+          predicateSpy
+        )
+        expect(predicateSpy).toHaveBeenCalled()
+      })
+
+      describe('> inserts are the right positions', () => {
+        describe('> predicate returns false', () => {
+          beforeEach(() => {
+            predicateSpy.and.returnValue(false)
+            linkedList.insertAfter(
+              obj3,
+              predicateSpy
+            )
+          })
+          it('> inserts at first position', () => {
+            expect(
+              linkedList.first.thisObj
+            ).toBe(obj3)
+          })
+
+          it('> first.prev is falsy', () => {
+            expect(
+              linkedList.first.prev
+            ).toBeFalsy()
+          })
+
+          it('> first.next.thisObj to be obj1', () => {
+            expect(
+              linkedList.first.next.thisObj
+            ).toBe(obj1)
+          })
+        })
+        describe('> predicate returns true for obj1', () => {
+          beforeEach(() => {
+            predicateSpy.and.callFake(function(){
+              return arguments[0].thisObj === obj1
+            })
+            linkedList.insertAfter(
+              obj3,
+              predicateSpy
+            )
+          })
+
+          it('> first.next is obj3', () => {
+            expect(
+              linkedList.first.next.thisObj
+            ).toBe(obj3)
+          })
+
+          it('> last.prev is obj3', () => {
+            expect(
+              linkedList.last.prev.thisObj
+            ).toBe(obj3)
+          })
+
+        })
+        describe('> predicate returns true for obj2', () => {
+          beforeEach(() => {
+            predicateSpy.and.callFake(function(){
+              return arguments[0].thisObj === obj2
+            })
+            linkedList.insertAfter(
+              obj3,
+              predicateSpy
+            )
+          })
+
+          it('> inserts at last', () => {
+            expect(
+              linkedList.last.thisObj
+            ).toBe(obj3)
+          })
+
+          it('> last.next is empty', () => {
+            expect(
+              linkedList.last.next
+            ).toBeFalsy()
+          })
+
+          it('> last.prev to be obj2', () => {
+            expect(
+              linkedList.last.prev.thisObj
+            ).toBe(obj2)
+          })
+        })
+      })
+    })
+  })
+
+  describe('> FindInLinkedList', () => {
+
+  })
+})
diff --git a/src/util/LinkedList.ts b/src/util/LinkedList.ts
new file mode 100644
index 0000000000000000000000000000000000000000..77e8e21e553ee16d74aebd343ee1c1ff6a81b3d9
--- /dev/null
+++ b/src/util/LinkedList.ts
@@ -0,0 +1,117 @@
+export interface IDoublyLinkedItem<T extends object> {
+  next: IDoublyLinkedItem<T>
+  prev: IDoublyLinkedItem<T>
+  thisObj: T
+  readonly index: number
+  list: DoublyLinkedList<T>
+}
+
+export class DoublyLinkedList<T extends object>{
+  
+  public first: IDoublyLinkedItem<T>
+  public last: IDoublyLinkedItem<T>
+  private _map = new WeakMap<T, IDoublyLinkedItem<T>>()
+  private _size: number = 0
+  insertAfter(element: T, predicate: (cmpObj: IDoublyLinkedItem<T>) => boolean){
+    if (this._map.get(element)) {
+      console.warn(`element has already been added to the doublylinkedlist`)
+      return
+    }
+
+    const insertAfter = FindInLinkedList<T>(
+      this,
+      predicate
+    )
+
+    /**
+     * if predicate can be found, then insert after the found entry
+     * if not, then the previous first entry will be the next element
+     */
+    const newDoublyLinkedItemNext = insertAfter
+      ? insertAfter.next
+      : this.first
+
+    const newDoublyLinkedItem: IDoublyLinkedItem<T> = {
+      prev: insertAfter,
+      next: newDoublyLinkedItemNext,
+      thisObj: element,
+      get index() {
+        let count = 0, prev: IDoublyLinkedItem<T>
+        prev = this.prev
+        while (prev) {
+          prev = prev.prev
+          count ++
+        }
+        return count
+      },
+      list: this
+    }
+
+    /**
+    * set next of prev item
+    * if prev is null, set first as this doublyitem
+    */
+    if (insertAfter) insertAfter.next = newDoublyLinkedItem
+    else this.first = newDoublyLinkedItem
+
+    /**
+    * set prev of next item
+    * if next is null, set last as this doublyitem
+    */
+    if (newDoublyLinkedItemNext) newDoublyLinkedItemNext.prev = newDoublyLinkedItem
+    else this.last = newDoublyLinkedItem
+    
+    this._map.set(element, newDoublyLinkedItem)
+    this._size ++
+  }
+  remove(element: T) {
+    const doublyLinkedItem = this._map.get(element)
+    if (!doublyLinkedItem) {
+      console.error(`doubly linked item not found`)
+      return
+    }
+    const { next, prev } = doublyLinkedItem
+
+    if (prev) prev.next = next
+    if (next) next.prev = prev
+
+    if (doublyLinkedItem === this.first) this.first = this.first.next
+    if (doublyLinkedItem === this.last) this.last = this.last.prev
+
+    // weakmap does not need to explicitly remove key/val
+    // decrement the size
+    this._size --
+  }
+  size(){
+    return this._size
+  }
+
+  get(idx: number) {
+    if (idx >= this.size()) throw new Error(`Index out of bound!`)
+    let i = 0
+    let curr = this.first
+    while (i < idx) {
+      curr = curr.next
+      if (!curr) throw new Error(`Index out of bound!`)
+      i ++
+    }
+    return curr
+  }
+}
+
+export function FindInLinkedList<T extends object>(list: DoublyLinkedList<T>, predicate: (element: IDoublyLinkedItem<T>) => boolean){
+  let compareObj = list.first,
+    returnObj: IDoublyLinkedItem<T> = null
+
+  if (!compareObj) return null
+
+  do {
+    if (predicate(compareObj)) {
+      returnObj = compareObj
+      break
+    }
+    compareObj = compareObj.next
+  } while(!!compareObj)
+
+  return returnObj
+}
diff --git a/src/util/constants.ts b/src/util/constants.ts
index 0236f4e41a896222c77208711a82b836d7f18aae..7a8bb4a12b67fa82447a8d82749dc5d319489c87 100644
--- a/src/util/constants.ts
+++ b/src/util/constants.ts
@@ -7,6 +7,7 @@ export const LOCAL_STORAGE_CONST = {
   MOBILE_UI: 'fzj.xg.iv.MOBILE_UI',
   AGREE_COOKIE: 'fzj.xg.iv.AGREE_COOKIE',
   AGREE_KG_TOS: 'fzj.xg.iv.AGREE_KG_TOS',
+  QUICK_TOUR_VIEWED: 'fzj.dg.iv.QUICK_TOUR_VIEWED',
 
   FAV_DATASET: 'fzj.xg.iv.FAV_DATASET_V2',
 }
@@ -41,7 +42,7 @@ export const appendScriptFactory = (document: Document) => {
   })
 }
 
-export const REMOVE_SCRIPT_TOKEN: InjectionToken<(el: HTMLScriptElement) => void> = new InjectionToken(`REMOVE_SCRIPT_TOKEN`) 
+export const REMOVE_SCRIPT_TOKEN: InjectionToken<(el: HTMLScriptElement) => void> = new InjectionToken(`REMOVE_SCRIPT_TOKEN`)
 
 export const removeScriptFactory = (document: Document) => {
   return (srcEl: HTMLScriptElement) => {
@@ -73,10 +74,10 @@ import { EnumColorMapName, mapKeyColorMap } from './colorMaps'
 import { InjectionToken } from "@angular/core"
 
 export const getShader = ({
-  colormap = EnumColorMapName.GREYSCALE, 
+  colormap = EnumColorMapName.GREYSCALE,
   lowThreshold = 0,
   highThreshold = 1,
-  brightness = 0, 
+  brightness = 0,
   contrast = 0,
   removeBg = false
 } = {}): string => {
@@ -115,3 +116,17 @@ export const compareLandmarksChanged: (prevLandmarks: any[], newLandmarks: any[]
 }
 
 export const CYCLE_PANEL_MESSAGE = `[spacebar] to cycle through views`
+export const BS_ENDPOINT = new InjectionToken<string>('BS_ENDPOINT')
+
+export const UNSUPPORTED_PREVIEW = [{
+  text: 'Preview of Colin 27 and JuBrain Cytoarchitectonic',
+  previewSrc: './res/image/1.png',
+}, {
+  text: 'Preview of Big Brain 2015 Release',
+  previewSrc: './res/image/2.png',
+}, {
+  text: 'Preview of Waxholm Rat V2.0',
+  previewSrc: './res/image/3.png',
+}]
+
+export const UNSUPPORTED_INTERVAL = 7000
diff --git a/src/util/directives/captureClickListener.directive.ts b/src/util/directives/captureClickListener.directive.ts
index b8e04e77f862b0841fb23cac3b801b494a729809..1a9fb9b583b34f9c1c0c5d6cab929636951da35d 100644
--- a/src/util/directives/captureClickListener.directive.ts
+++ b/src/util/directives/captureClickListener.directive.ts
@@ -27,9 +27,9 @@ export class CaptureClickListenerDirective implements OnInit, OnDestroy {
   }
 
   public ngOnInit() {
-    const mouseDownObs$ = fromEvent(this.element, 'mousedown', { capture: this.captureDocument })
-    const mouseMoveObs$ = fromEvent(this.element, 'mousemove', { capture: this.captureDocument })
-    const mouseUpObs$ = fromEvent(this.element, 'mouseup', { capture: this.captureDocument })
+    const mouseDownObs$ = fromEvent(this.element, 'pointerdown', { capture: this.captureDocument })
+    const mouseMoveObs$ = fromEvent(this.element, 'pointermove', { capture: this.captureDocument })
+    const mouseUpObs$ = fromEvent(this.element, 'pointerup', { capture: this.captureDocument })
 
     this.subscriptions.push(
       mouseDownObs$.subscribe(event => {
diff --git a/src/util/fn.spec.ts b/src/util/fn.spec.ts
index 98f60a54cb0b71f9e5de4c38db0f8b6a8d95f6e2..bbdbff61026839a3ce6a020d75bc25a4a5cd1aca 100644
--- a/src/util/fn.spec.ts
+++ b/src/util/fn.spec.ts
@@ -1,8 +1,33 @@
+import { fakeAsync, tick } from '@angular/core/testing'
 import {} from 'jasmine'
-import { isSame } from './fn'
+import { hot } from 'jasmine-marbles'
+import { Observable, of } from 'rxjs'
+import { switchMap } from 'rxjs/operators'
+import { isSame, getGetRegionFromLabelIndexId, switchMapWaitFor, bufferUntil } from './fn'
 
-describe(`util/fn.ts`, () => {
-  describe(`#isSame`, () => {
+describe(`> util/fn.ts`, () => {
+
+  describe('> #getGetRegionFromLabelIndexId', () => {
+    const colinsJson = require('!json-loader!../res/ext/colin.json')
+    
+    const COLIN_JULICHBRAIN_LAYER_NAME = `COLIN_V25_LEFT_NG_SPLIT_HEMISPHERE`
+    const COLIN_V25_ID = 'minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-26'
+    
+    it('translateds hoc1 from labelIndex to region', () => {
+
+      const getRegionFromlabelIndexId = getGetRegionFromLabelIndexId({
+        parcellation: {
+          ...colinsJson.parcellations.find(p => p['@id'] === COLIN_V25_ID),
+          updated: true,
+        },
+      })
+      const fetchedRegion = getRegionFromlabelIndexId({ labelIndexId: `${COLIN_JULICHBRAIN_LAYER_NAME}#116` })
+      expect(fetchedRegion).toBeTruthy()
+      expect(fetchedRegion.fullId.kg.kgId).toEqual('c9753e82-80ca-4074-a704-9dd2c4c0d58b')
+      
+    })
+  })
+  describe(`> #isSame`, () => {
     it('should return true with null, null', () => {
       expect(isSame(null, null)).toBe(true)
     })
@@ -28,4 +53,78 @@ describe(`util/fn.ts`, () => {
       expect(obj).not.toEqual(obj2)
     })
   })
+
+  describe('> #switchMapWaitFor', () => {
+    const val = 'hello world'
+    describe('> if condition is true to begin', () => {
+      const conditionFn = jasmine.createSpy()
+      beforeEach(() => {
+        conditionFn.and.returnValue(true)
+      })
+      afterEach(() => {
+        conditionFn.calls.reset()
+      })
+      it('> should wait for 16 ms then emit', fakeAsync(() => {
+        const obs$ = of(val).pipe(
+          switchMap(switchMapWaitFor({
+            condition: conditionFn
+          }))
+        )
+        obs$.subscribe(ex => {
+          expect(conditionFn).toHaveBeenCalled()
+          expect(ex).toEqual(val)
+        })
+        tick(200)
+      }))
+    })
+  })
+
+  describe('> #bufferUntil', () => {
+    let src: Observable<number>
+    beforeEach(() => {
+      src = hot('a-b-c|', {
+        a: 1,
+        b: 2,
+        c: 3,
+      })
+    })
+    it('> outputs array of original emitted value', () => {
+
+      expect(
+        src.pipe(
+          bufferUntil({
+            condition: () => true,
+            leading: true,
+          })
+        )
+      ).toBeObservable(
+        hot('a-b-c|', {
+          a: [1],
+          b: [2],
+          c: [3],
+        })
+      )
+    })
+
+    it('> on condition success, emit all in array', () => {
+
+      let counter = 0
+      expect(
+        src.pipe(
+          bufferUntil({
+            condition: () => {
+              counter ++
+              return counter > 2
+            },
+            leading: true,
+            interval: 60000,
+          })
+        )
+      ).toBeObservable(
+        hot('----c|', {
+          c: [1,2,3],
+        })
+      )
+    })
+  })
 })
diff --git a/src/util/fn.ts b/src/util/fn.ts
index c589873f2ea1662fefb9c6557c9a3a58fd3602f5..db3ad47ce6e43a8065bef6acc0459be9a72d5d99 100644
--- a/src/util/fn.ts
+++ b/src/util/fn.ts
@@ -1,3 +1,7 @@
+import { deserialiseParcRegionId } from 'common/util'
+import { interval, Observable, of } from 'rxjs'
+import { filter, mapTo, take } from 'rxjs/operators'
+
 export function isSame(o, n) {
   if (!o) { return !n }
   return o === n || (o && n && o.name === n.name)
@@ -31,3 +35,295 @@ export function getNgIds(regions: any[]): string[] {
       .filter(ngId => !!ngId)
     : []
 }
+
+const recursiveFlatten = (region, {ngId}) => {
+  return [{
+    ngId,
+    ...region,
+  }].concat(
+    ...((region.children && region.children.map && region.children.map(c => recursiveFlatten(c, { ngId : region.ngId || ngId })) ) || []),
+  )
+}
+
+export function recursiveFindRegionWithLabelIndexId({ regions, labelIndexId, inheritedNgId = 'root' }: {regions: any[], labelIndexId: string, inheritedNgId: string}) {
+  const { ngId, labelIndex } = deserialiseParcRegionId( labelIndexId )
+  const fr1 = regions.map(r => recursiveFlatten(r, { ngId: inheritedNgId }))
+  const fr2 = fr1.reduce((acc, curr) => acc.concat(...curr), [])
+  const found = fr2.find(r => r.ngId === ngId && Number(r.labelIndex) === Number(labelIndex))
+  if (found) { return found }
+  return null
+}
+
+export function getUuid(){
+  return crypto.getRandomValues(new Uint32Array(1))[0].toString(16)
+}
+
+export const getGetRegionFromLabelIndexId = ({ parcellation }) => {
+  const { ngId: defaultNgId, regions } = parcellation
+  // if (!updated) throw new Error(`parcellation not yet updated`)
+  return ({ labelIndexId }) =>
+    recursiveFindRegionWithLabelIndexId({ regions, labelIndexId, inheritedNgId: defaultNgId })
+}
+
+type TPrimitive = string | number
+
+const include = <T extends TPrimitive>(el: T, arr: T[]) => arr.indexOf(el) >= 0
+export const arrayOfPrimitiveEqual = <T extends TPrimitive>(o: T[], n: T[]) =>
+  o.every(el => include(el, n))
+  && n.every(el => include(el, o))
+
+interface ISwitchMapWaitFor {
+  interval?: number
+  leading?: boolean
+  condition: (arg?: any) => boolean
+}
+export function switchMapWaitFor<T>(opts: ISwitchMapWaitFor){
+  return (arg: T) => {
+    if (opts.leading && opts.condition(arg)) return of(arg)
+    return interval(opts.interval || 16).pipe(
+      filter(() => opts.condition(arg)),
+      take(1),
+      mapTo(arg)
+    )
+  }
+}
+
+
+type TCacheFunctionArg = {
+  serialization?: (...arg: any[]) => string
+}
+
+/**
+ * Member function decorator
+ * Multiple function calls with strictly equal arguments will return cached result
+ * @returns cached result if exists, else call original function
+ */
+export const CachedFunction = (config?: TCacheFunctionArg) => {
+  const { serialization } = config || {}
+  const cache = {}
+  const cachedValKeySym = Symbol('cachedValKeySym')
+  return (_target: Record<string, any>, _propertyKey: string, descriptor: PropertyDescriptor) => {
+    const originalMethod = descriptor.value
+    descriptor.value = function(...args: any[]) {
+      let found = cache
+      if (serialization) {
+        const key = serialization(...args)
+        if (!cache[key]) cache[key] = {}
+        found = cache[key]
+      } else {
+        for (const arg of args) {
+          if (!cache[arg]) cache[arg] = {}
+          found = cache[arg]
+        }
+      }
+      if (found[cachedValKeySym]) return found[cachedValKeySym]
+      const returnVal = originalMethod.apply(this, args)
+      found[cachedValKeySym] = returnVal
+      return returnVal
+    }
+  }
+}
+
+// A quick, non security hash function
+export class QuickHash {
+  private length = 6
+  constructor(opts?: any){
+    if (opts?.length) this.length = opts.length
+  }
+
+  @CachedFunction()
+  getHash(str: string){
+    let hash = 0
+    for (const char of str) {
+      const charCode = char.charCodeAt(0)
+      hash = ((hash << 5) - hash) + charCode
+      hash = hash & hash
+    }
+    return hash.toString(16).slice(1)
+  }
+}
+
+// fsaverage uses threesurfer, which, whilst do not use ngId, uses 'left' and 'right' as keys 
+const fsAverageKeyVal = {
+  "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-290": {
+    "left hemisphere": "left",
+    "right hemisphere": "right"
+  }
+}
+
+/**
+ * in order to maintain backwards compat with url encoding of selected regions
+ * TODO setup a sentry to catch if these are ever used. if not, retire the hard coding 
+ */
+const BACKCOMAP_KEY_DICT = {
+
+  // human multi level
+  'juelich/iav/atlas/v1.0.0/1': {
+    // icbm152
+    'minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2': {
+      // julich brain v2.6
+      'minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-26': {
+        'left hemisphere': 'MNI152_V25_LEFT_NG_SPLIT_HEMISPHERE',
+        'right hemisphere': 'MNI152_V25_RIGHT_NG_SPLIT_HEMISPHERE'
+      },
+      // bundle hcp
+      // even though hcp, long/short bundle, and difumo has no hemisphere distinctions, the way siibra-python parses the region,
+      // and thus attributes left/right hemisphere, still results in some regions being parsed as left/right hemisphere
+      "juelich/iav/atlas/v1.0.0/79cbeaa4ee96d5d3dfe2876e9f74b3dc3d3ffb84304fb9b965b1776563a1069c": {
+        "whole brain": "superficial-white-bundle-HCP",
+        "left hemisphere": "superficial-white-bundle-HCP",
+        "right hemisphere": "superficial-white-bundle-HCP"
+      },
+      // julich brain v1.18
+      "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579": {
+        "left hemisphere": "jubrain mni152 v18 left",
+        "right hemisphere": "jubrain mni152 v18 right",
+      },
+      // long bundle
+      "juelich/iav/atlas/v1.0.0/5": {
+        "whole brain": "fibre bundle long",
+        "left hemisphere": "fibre bundle long",
+        "right hemisphere": "fibre bundle long",
+      },
+      // bundle short
+      "juelich/iav/atlas/v1.0.0/6": {
+        "whole brain": "fibre bundle short",
+        "left hemisphere": "fibre bundle short",
+        "right hemisphere": "fibre bundle short",
+      },
+      // difumo 64
+      "minds/core/parcellationatlas/v1.0.0/d80fbab2-ce7f-4901-a3a2-3c8ef8a3b721": {
+        "whole brain": "DiFuMo Atlas (64 dimensions)",
+        "left hemisphere": "DiFuMo Atlas (64 dimensions)",
+        "right hemisphere": "DiFuMo Atlas (64 dimensions)",
+      },
+      "minds/core/parcellationatlas/v1.0.0/73f41e04-b7ee-4301-a828-4b298ad05ab8": {
+        "whole brain": "DiFuMo Atlas (128 dimensions)",
+        "left hemisphere": "DiFuMo Atlas (128 dimensions)",
+        "right hemisphere": "DiFuMo Atlas (128 dimensions)",
+      },
+      "minds/core/parcellationatlas/v1.0.0/141d510f-0342-4f94-ace7-c97d5f160235": {
+        "whole brain": "DiFuMo Atlas (256 dimensions)",
+        "left hemisphere": "DiFuMo Atlas (256 dimensions)",
+        "right hemisphere": "DiFuMo Atlas (256 dimensions)",
+      },
+      "minds/core/parcellationatlas/v1.0.0/63b5794f-79a4-4464-8dc1-b32e170f3d16": {
+        "whole brain": "DiFuMo Atlas (512 dimensions)",
+        "left hemisphere": "DiFuMo Atlas (512 dimensions)",
+        "right hemisphere": "DiFuMo Atlas (512 dimensions)",
+      },
+      "minds/core/parcellationatlas/v1.0.0/12fca5c5-b02c-46ce-ab9f-f12babf4c7e1": {
+        "whole brain": "DiFuMo Atlas (1024 dimensions)",
+        "left hemisphere": "DiFuMo Atlas (1024 dimensions)",
+        "right hemisphere": "DiFuMo Atlas (1024 dimensions)",
+      },
+    },
+    // colin 27
+    "minds/core/referencespace/v1.0.0/7f39f7be-445b-47c0-9791-e971c0b6d992": {
+      "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-26": {
+        "left hemisphere": "COLIN_V25_LEFT_NG_SPLIT_HEMISPHERE",
+        "right hemisphere": "COLIN_V25_RIGHT_NG_SPLIT_HEMISPHERE",
+      },
+      "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579": {
+        "left hemisphere": "jubrain colin v18 left",
+        "right hemisphere": "jubrain colin v18 right",
+      }
+    },
+    // big brain
+    "minds/core/referencespace/v1.0.0/a1655b99-82f1-420f-a3c2-fe80fd4c8588": {
+      "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-26": {
+
+      },
+      // isocortex
+      "juelich/iav/atlas/v1.0.0/4": {
+        "whole brain": " tissue type: "
+      },
+      // cortical layers
+      "juelich/iav/atlas/v1.0.0/3": {
+        "whole brain": "cortical layers"
+      },
+    },
+
+    // fsaverage
+    "minds/core/referencespace/v1.0.0/tmp-fsaverage": fsAverageKeyVal,
+  }
+}
+
+export class MultiDimMap{
+  
+  private map = new Map()
+
+  static KeyHash = new QuickHash()
+
+  static GetProxyKeyMatch(...arg: any[]): string {
+
+    let proxyKeyMatch = BACKCOMAP_KEY_DICT
+    for (let i = 0; i < arg.length; i++) {
+      if (proxyKeyMatch) proxyKeyMatch = proxyKeyMatch[arg[i]]
+    }
+    if (proxyKeyMatch) return proxyKeyMatch as any
+    return null
+  }
+
+  static GetKey(...arg: any[]){
+    let mapKey = ``
+    for (let i = 0; i < arg.length; i++) {
+      mapKey += arg[i]
+    }
+    return MultiDimMap.KeyHash.getHash(mapKey)
+  }
+
+  set(...arg: any[]) {
+    const mapKey = MultiDimMap.GetKey(...(arg.slice(0, -1)))
+    this.map.set(mapKey, arg[arg.length - 1])
+  }
+  get(...arg: any[]) {
+    const mapKey = MultiDimMap.GetKey(...arg)
+    return this.map.get(mapKey)
+  }
+  delete(...arg: any[]) {
+    const mapKey = MultiDimMap.GetKey(...arg)
+    return this.map.delete(mapKey)
+  }
+}
+
+export function recursiveMutate<T>(arr: T[], getChildren: (obj: T) => T[], mutateFn: (obj: T) => void){
+  for (const obj of arr) {
+    mutateFn(obj)
+    recursiveMutate(
+      getChildren(obj),
+      getChildren,
+      mutateFn
+    )
+  }
+}
+
+export function bufferUntil<T>(opts: ISwitchMapWaitFor) {
+  const { condition, leading, interval: int = 160 } = opts
+  let buffer: T[] = []
+  return (src: Observable<T>) => new Observable<T[]>(obs => {
+    const sub = interval(int).pipe(
+      filter(() => buffer.length > 0)
+    ).subscribe(() => {
+      if (condition()) {
+        obs.next(buffer)
+        buffer = []
+      }
+    })
+    src.subscribe(
+      val => {
+        if (leading && condition()) {
+          obs.next([...buffer, val])
+          buffer = []
+        } else {
+          buffer.push(val)
+        }
+      },
+      err => obs.error(err),
+      () => {
+        obs.complete()
+        sub.unsubscribe()
+      }
+    )
+  })
+}
diff --git a/src/util/index.ts b/src/util/index.ts
index 25e810ea70b989a4e67ea70ae107fb598444037e..21b7d4d11094b30d4e750237e94ebc32a516cdce 100644
--- a/src/util/index.ts
+++ b/src/util/index.ts
@@ -1,3 +1,8 @@
 export { UtilModule } from './util.module'
 export { PureContantService } from './pureConstant.service'
-export { CLICK_INTERCEPTOR_INJECTOR, ClickInterceptor } from './injectionTokens'
+export { CLICK_INTERCEPTOR_INJECTOR, ClickInterceptor, CONTEXT_MENU_ITEM_INJECTOR, TClickInterceptorConfig, TContextMenu } from './injectionTokens'
+export {
+  DoublyLinkedList,
+  FindInLinkedList,
+  IDoublyLinkedItem
+} from './LinkedList'
diff --git a/src/util/injectionTokens.ts b/src/util/injectionTokens.ts
index c910f15350fec0dd087c674d54688dd4ccfe882f..eed3fbcb10bfe65e71ca23dfab5c3db621778159 100644
--- a/src/util/injectionTokens.ts
+++ b/src/util/injectionTokens.ts
@@ -2,7 +2,18 @@ import { InjectionToken } from "@angular/core";
 
 export const CLICK_INTERCEPTOR_INJECTOR = new InjectionToken<ClickInterceptor>('CLICK_INTERCEPTOR_INJECTOR')
 
+export type TClickInterceptorConfig = {
+  last?: boolean
+}
+
 export interface ClickInterceptor{
-  register: (interceptorFunction: (ev: any, next: Function) => void) => void
+  register: (interceptorFunction: (ev: any) => boolean, config?: TClickInterceptorConfig) => void
   deregister: (interceptorFunction: Function) => void
 }
+
+export const CONTEXT_MENU_ITEM_INJECTOR = new InjectionToken('CONTEXT_MENU_ITEM_INJECTOR')
+
+export type TContextMenu<T> = {
+  register: (fn: T) => void
+  deregister: (fn: (fn: T) => void) => void
+}
diff --git a/src/util/interfaces.ts b/src/util/interfaces.ts
index 0486e1159e61f8280456a32abd44a76df37f5051..27b92d2abc203d2d1d56b5bb71053154aaa297a6 100644
--- a/src/util/interfaces.ts
+++ b/src/util/interfaces.ts
@@ -14,7 +14,7 @@ export interface IHasFullId{
 }
 
 
-export type TOverwriteShowDatasetDialog = (dataset: { fullId: string } | { name: string, description: string }) => void
+export type TOverwriteShowDatasetDialog = (arg: any) => void
 
 export const OVERWRITE_SHOW_DATASET_DIALOG_TOKEN = new InjectionToken<TOverwriteShowDatasetDialog>('OVERWRITE_SHOW_DATASET_DIALOG_TOKEN')
 
diff --git a/src/util/pipes/filterArray.pipe.ts b/src/util/pipes/filterArray.pipe.ts
index eca8e461026de20361e62e71eeefe4d76ba96d37..5e7c9653d73430b7631b78cd7a739a2a2ca13c75 100644
--- a/src/util/pipes/filterArray.pipe.ts
+++ b/src/util/pipes/filterArray.pipe.ts
@@ -1,11 +1,12 @@
 import { Pipe, PipeTransform } from "@angular/core";
 
 @Pipe({
-  name: 'filterArray'
+  name: 'filterArray',
+  pure: true
 })
 
 export class FilterArrayPipe implements PipeTransform{
   public transform<T>(arr: T[], filterFn: (item: T, index: number, array: T[]) => boolean){
-    return arr.filter(filterFn)
+    return (arr || []).filter(filterFn)
   }
 }
diff --git a/src/ui/layerbrowser/getFilename.pipe.ts b/src/util/pipes/getFilename.pipe.ts
similarity index 82%
rename from src/ui/layerbrowser/getFilename.pipe.ts
rename to src/util/pipes/getFilename.pipe.ts
index 41469ab435b04c222cf95a353ac0680edcbf72e4..f8cc4891f2f8ddba7f7eb9688203de483301586f 100644
--- a/src/ui/layerbrowser/getFilename.pipe.ts
+++ b/src/util/pipes/getFilename.pipe.ts
@@ -5,7 +5,7 @@ import { Pipe, PipeTransform } from "@angular/core";
 })
 
 export class GetFilenamePipe implements PipeTransform {
-  private regex: RegExp = new RegExp('[\\/\\\\]([\\w\\.]*?)$')
+  private regex: RegExp = new RegExp('/([^/]+)$')
   public transform(fullname: string): string {
     return this.regex.test(fullname)
       ? this.regex.exec(fullname)[1]
diff --git a/src/util/pipes/objToArray.pipe.spec.ts b/src/util/pipes/objToArray.pipe.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d1a54e0822e710a18cad06ccc9c619423d528a52
--- /dev/null
+++ b/src/util/pipes/objToArray.pipe.spec.ts
@@ -0,0 +1,17 @@
+import { ObjectToArrayPipe } from "./objToArray.pipe"
+
+describe('> objToArray.pipe.ts', () => {
+  describe('> ObjectToArrayPipe', () => {
+    const pipe = new ObjectToArrayPipe()
+    it('> transforms obj to array', () => {
+      const result = pipe.transform({'a': '1', 'b': '2'})
+      expect(result).toEqual([{
+        key: 'a',
+        value: '1'
+      }, {
+        key: 'b',
+        value: '2'
+      }])
+    })
+  })
+})
\ No newline at end of file
diff --git a/src/util/pipes/objToArray.pipe.ts b/src/util/pipes/objToArray.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9320b363193d65dab5ae50b19461ae698ea35c00
--- /dev/null
+++ b/src/util/pipes/objToArray.pipe.ts
@@ -0,0 +1,22 @@
+import { Pipe, PipeTransform } from "@angular/core";
+
+interface ITransformedObj{
+  key: string
+  value: string
+}
+
+@Pipe({
+  name: 'objToArray',
+  pure: true
+})
+
+export class ObjectToArrayPipe implements PipeTransform{
+  public transform(input: { [key: string]: any }): ITransformedObj[]{
+    return Object.keys(input).map(key => {
+      return {
+        key,
+        value: input[key]
+      }
+    })
+  }
+}
\ No newline at end of file
diff --git a/src/util/pluginHandlerClasses/modalHandler.ts b/src/util/pluginHandlerClasses/modalHandler.ts
deleted file mode 100644
index 2afa36774891ced56a784d685f02dd159570ec58..0000000000000000000000000000000000000000
--- a/src/util/pluginHandlerClasses/modalHandler.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-export class ModalHandler {
-
-  public hide: () => void
-  public show: () => void
-  // onHide : (callback: () => void) => void
-  // onHidden : (callback : () => void) => void
-  // onShow : (callback : () => void) => void
-  // onShown : (callback : () => void) => void
-  public title: string
-  public body: string
-  public footer: string
-
-  public dismissable = true
-}
diff --git a/src/util/pluginHandlerClasses/toastHandler.ts b/src/util/pluginHandlerClasses/toastHandler.ts
deleted file mode 100644
index 9bba88be2bd00bc936ba0da7609054ddc5ea769d..0000000000000000000000000000000000000000
--- a/src/util/pluginHandlerClasses/toastHandler.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-export class ToastHandler {
-  public message = 'Toast message'
-  public timeout = 3000
-  public dismissable = true
-  public show: () => void
-  public hide: () => void
-  public htmlMessage: string
-}
diff --git a/src/util/pureConstant.service.spec.ts b/src/util/pureConstant.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2ca6fb127878caace8d1fdf0d5049a65bd2cc2af
--- /dev/null
+++ b/src/util/pureConstant.service.spec.ts
@@ -0,0 +1,79 @@
+import { HttpClientTestingModule, HttpTestingController } from "@angular/common/http/testing"
+import { TestBed } from "@angular/core/testing"
+import { MockStore, provideMockStore } from "@ngrx/store/testing"
+import { hot } from "jasmine-marbles"
+import { BS_ENDPOINT } from "src/atlasComponents/regionalFeatures/bsFeatures"
+import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service"
+import { viewerStateFetchedAtlasesSelector, viewerStateFetchedTemplatesSelector } from "src/services/state/viewerState/selectors"
+import { PureContantService } from "./pureConstant.service"
+import { TAtlas } from "./siibraApiConstants/types"
+
+const MOCK_BS_ENDPOINT = `http://localhost:1234`
+
+describe('> pureConstant.service.ts', () => {
+  describe('> PureContantService', () => {
+    let httpController: HttpTestingController
+    beforeEach(() => {
+      TestBed.configureTestingModule({
+        imports:[
+          HttpClientTestingModule,
+        ],
+        providers: [
+          provideMockStore(),
+          {
+            provide: AtlasWorkerService,
+            useValue: {
+              worker: null
+            }
+          },
+          {
+            provide: BS_ENDPOINT,
+            useValue: MOCK_BS_ENDPOINT
+          }
+        ]
+      })
+
+      const mockStore = TestBed.inject(MockStore)
+      mockStore.overrideSelector(viewerStateFetchedTemplatesSelector, [])
+      mockStore.overrideSelector(viewerStateFetchedAtlasesSelector, [])
+      httpController = TestBed.inject(HttpTestingController)
+    })
+
+    afterEach(() => {
+      httpController.verify()
+    })
+
+    it('> can be init', () => {
+      const service = TestBed.inject(PureContantService)
+      const exp = httpController.expectOne(`${MOCK_BS_ENDPOINT}/atlases`)
+      exp.flush([])
+      expect(service).toBeTruthy()
+    })
+    describe('> allFetchingReady$', () => {
+      const mockAtlas: TAtlas = {
+        id: 'mockatlas id',
+        name: 'mockatlas name',
+        links: {
+          parcellations: {
+            href: `${MOCK_BS_ENDPOINT}/mockatlas-parcellation-href`
+          },
+          spaces: {
+            href: `${MOCK_BS_ENDPOINT}/atlas-spaces`
+          }
+        }
+      }
+      it('> can be init, and configuration emits allFetchingReady$', () => {
+        const service = TestBed.inject(PureContantService)
+        const exp = httpController.expectOne(`${MOCK_BS_ENDPOINT}/atlases`)
+        exp.flush([mockAtlas])
+        service.allFetchingReady$.subscribe()
+
+        const expT1 = httpController.expectOne(`${MOCK_BS_ENDPOINT}/atlases/${encodeURIComponent(mockAtlas.id)}/spaces`)
+        expT1.flush([])
+        const expP1 = httpController.expectOne(`${MOCK_BS_ENDPOINT}/atlases/${encodeURIComponent(mockAtlas.id)}/parcellations`)
+        expP1.flush([])
+      })
+  
+    })
+  })
+})
diff --git a/src/util/pureConstant.service.ts b/src/util/pureConstant.service.ts
index bc713ff19700ac01da3cda37f0f230daa817b872..f15f92263977965fadb64d168fcab5c81a9fd597 100644
--- a/src/util/pureConstant.service.ts
+++ b/src/util/pureConstant.service.ts
@@ -1,15 +1,157 @@
-import { Injectable, OnDestroy } from "@angular/core";
-import { Store, createSelector, select } from "@ngrx/store";
-import { Observable, merge, Subscription, of, throwError, forkJoin, fromEvent } from "rxjs";
-import { VIEWER_CONFIG_FEATURE_KEY, IViewerConfigState, viewerConfigSelectorUseMobileUi } from "src/services/state/viewerConfig.store.helper";
-import { shareReplay, tap, scan, catchError, filter, switchMap, map, take } from "rxjs/operators";
+import { Inject, Injectable, OnDestroy } from "@angular/core";
+import { Store, select } from "@ngrx/store";
+import { Observable, Subscription, of, forkJoin, fromEvent, combineLatest } 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";
-import { BACKENDURL } from './constants'
-import { viewerStateSetFetchedAtlases } from "src/services/state/viewerState.store.helper";
+import { viewerStateFetchedTemplatesSelector, viewerStateSetFetchedAtlases } from "src/services/state/viewerState.store.helper";
 import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service";
 import { LoggingService } from "src/logging";
+import { viewerStateFetchedAtlasesSelector, viewerStateSelectedTemplateSelector } from "src/services/state/viewerState/selectors";
+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";
 
-const getUniqueId = () => Math.round(Math.random() * 1e16).toString(16)
+function getNgId(atlasId: string, tmplId: string, parcId: string, regionKey: string){
+  const proxyId = MultiDimMap.GetProxyKeyMatch(atlasId, tmplId, parcId, regionKey)
+  if (proxyId) return proxyId
+  return '_' + MultiDimMap.GetKey(atlasId, tmplId, parcId, regionKey)
+}
+
+function parseId(id: TId){
+  if (typeof id === 'string') return id
+  return `${id.kg.kgSchema}/${id.kg.kgId}`
+}
+
+type THasId = {
+  ['@id']: string
+  name: string
+}
+
+type TIAVAtlas = {
+  templateSpaces: ({ availableIn: THasId[] } & THasId)[]
+  parcellations: ({
+    availableIn: THasId[]
+    baseLayer: boolean
+    '@version': {
+      name: string
+      '@next': string
+      '@previous': string
+      '@this': string
+    }
+  } & THasId)[]
+} & THasId
+
+const spaceMiscInfoMap = new Map([
+  ['minds/core/referencespace/v1.0.0/a1655b99-82f1-420f-a3c2-fe80fd4c8588', {
+    name: 'bigbrain',
+    scale: 1,
+  }],
+  ['minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2', {
+    name: 'icbm2009c',
+    scale: 1,
+  }],
+  ['minds/core/referencespace/v1.0.0/7f39f7be-445b-47c0-9791-e971c0b6d992', {
+    name: 'colin27',
+    scale: 1,
+  }],
+  ['minds/core/referencespace/v1.0.0/265d32a0-3d84-40a5-926f-bf89f68212b9', {
+    name: 'allen-mouse',
+    scale: 0.1,
+  }],
+  ['minds/core/referencespace/v1.0.0/d5717c4a-0fa1-46e6-918c-b8003069ade8', {
+    name: 'waxholm',
+    scale: 0.1,
+  }],
+])
+
+function getNehubaConfig(space: TSpaceFull) {
+
+  const darkTheme = space.src_volume_type === 'mri'
+  const { scale } = spaceMiscInfoMap.get(space.id) || { scale: 1 }
+  const backgrd = darkTheme
+    ? [0,0,0,1]
+    : [1,1,1,1]
+
+  const rmPsp = darkTheme
+    ? {"mode":"<","color":[0.1,0.1,0.1,1]}
+    :{"color":[1,1,1,1],"mode":"=="}
+  const drawSubstrates = darkTheme
+    ? {"color":[0.5,0.5,1,0.2]}
+    : {"color":[0,0,0.5,0.15]}
+  const drawZoomLevels = darkTheme
+    ? {"cutOff":150000 * scale }
+    : {"cutOff":200000 * scale,"color":[0.5,0,0,0.15] }
+
+  // enable surface parcellation
+  // otherwise, on segmentation selection, the unselected meshes will also be invisible
+  const surfaceParcellation = space.id === 'minds/core/referencespace/v1.0.0/7f39f7be-445b-47c0-9791-e971c0b6d992'
+  return {
+    "configName": "",
+    "globals": {
+      "hideNullImageValues": true,
+      "useNehubaLayout": {
+        "keepDefaultLayouts": false
+      },
+      "useNehubaMeshLayer": true,
+      "rightClickWithCtrlGlobal": false,
+      "zoomWithoutCtrlGlobal": false,
+      "useCustomSegmentColors": true
+    },
+    "zoomWithoutCtrl": true,
+    "hideNeuroglancerUI": true,
+    "rightClickWithCtrl": true,
+    "rotateAtViewCentre": true,
+    "enableMeshLoadingControl": true,
+    "zoomAtViewCentre": true,
+    // "restrictUserNavigation": true,
+    "dataset": {
+      "imageBackground": backgrd,
+      "initialNgState": {
+        "showDefaultAnnotations": false,
+        "layers": {},
+        "navigation": {
+          "zoomFactor": 350000 * scale,
+        },
+        "perspectiveOrientation": [
+          0.3140767216682434,
+          -0.7418519854545593,
+          0.4988985061645508,
+          -0.3195493221282959
+        ],
+        "perspectiveZoom": 1922235.5293810747 * scale
+      }
+    },
+    "layout": {
+      "useNehubaPerspective": {
+        "perspectiveSlicesBackground": backgrd,
+        "removePerspectiveSlicesBackground": rmPsp,
+        "perspectiveBackground": backgrd,
+        "fixedZoomPerspectiveSlices": {
+          "sliceViewportWidth": 300,
+          "sliceViewportHeight": 300,
+          "sliceZoom": 563818.3562426177 * scale,
+          "sliceViewportSizeMultiplier": 2
+        },
+        "mesh": {
+          "backFaceColor": backgrd,
+          "removeBasedOnNavigation": true,
+          "flipRemovedOctant": true,
+          surfaceParcellation
+        },
+        "centerToOrigin": true,
+        "drawSubstrates": drawSubstrates,
+        "drawZoomLevels": drawZoomLevels,
+        "restrictZoomLevel": {
+          "minZoom": 1200000 * scale,
+          "maxZoom": 3500000 * scale
+        }
+      }
+    }
+  }
+  
+}
 
 @Injectable({
   providedIn: 'root'
@@ -17,94 +159,183 @@ const getUniqueId = () => Math.round(Math.random() * 1e16).toString(16)
 
 export class PureContantService implements OnDestroy{
   
+  private subscriptions: Subscription[] = []
+  public repoUrl = `https://github.com/HumanBrainProject/interactive-viewer`
+  public supportEmailAddress = `support@ebrains.eu`
+  public docUrl = `https://interactive-viewer.readthedocs.io/en/latest/`
+
+  public showHelpSupportText: string = `Did you encounter an issue?
+Send us an email: <a target = "_blank" href = "mailto:${this.supportEmailAddress}">${this.supportEmailAddress}</a>
+
+Raise/track issues at github repo: <a target = "_blank" href = "${this.repoUrl}">${this.repoUrl}</a>
+`
+
   public useTouchUI$: Observable<boolean>
-  public fetchedAtlases$: Observable<any>
   public darktheme$: Observable<boolean>
 
   public totalAtlasesLength: number
 
-  public backendUrl = (BACKEND_URL && `${BACKEND_URL}/`.replace(/\/\/$/, '/')) || `${window.location.origin}${window.location.pathname}`
+  public allFetchingReady$: Observable<boolean>
+
+  private atlasParcSpcRegionMap = new MultiDimMap()
+
+  private _backendUrl = (BACKEND_URL && `${BACKEND_URL}/`.replace(/\/\/$/, '/')) || `${window.location.origin}${window.location.pathname}`
+  get backendUrl() {
+    console.warn(`something is using backendUrl`)
+    return this._backendUrl
+  }
 
+  /**
+   * TODO remove
+   * when removing, also remove relevant worker code
+   */
   private workerUpdateParcellation$ = fromEvent(this.workerService.worker, 'message').pipe(
     filter((message: MessageEvent) => message && message.data && message.data.type === 'UPDATE_PARCELLATION_REGIONS'),
     map(({ data }) => data)
   )
 
-  private fetchTemplate = (templateUrl) => this.http.get(`${this.backendUrl}${templateUrl}`, { responseType: 'json' }).pipe(
-    switchMap((template: any) => {
-      if (template.nehubaConfig) { return of(template) }
-      if (template.nehubaConfigURL) { return this.http.get(`${this.backendUrl}${template.nehubaConfigURL}`, { responseType: 'json' }).pipe(
-        map(nehubaConfig => {
-          return {
-            ...template,
-            nehubaConfig,
-          }
-        }),
-      )
+  public getRegionDetail(atlasId: string, parcId: string, spaceId: string, region: any) {
+    return this.http.get<TRegionDetail>(
+      `${this.bsEndpoint}/atlases/${encodeURIComponent(atlasId)}/parcellations/${encodeURIComponent(parcId)}/regions/${encodeURIComponent(region.name)}`,
+      {
+        params: {
+          'space_id': spaceId
+        },
+        responseType: 'json'
       }
-      throwError('neither nehubaConfig nor nehubaConfigURL defined')
-    }),
-  )
+    )
+  }
 
-  private processTemplate = template => forkJoin(
-    template.parcellations.map(parcellation => {
+  private getRegions(atlasId: string, parcId: string, spaceId: string){
+    return this.http.get<TRegion[]>(
+      `${this.bsEndpoint}/atlases/${encodeURIComponent(atlasId)}/parcellations/${encodeURIComponent(parcId)}/regions`,
+      {
+        params: {
+          'space_id': spaceId
+        },
+        responseType: 'json'
+      }
+    )
+  }
 
-      const id = getUniqueId()
+  private getParcs(atlasId: string){
+    return this.http.get<TParc[]>(
+      `${this.bsEndpoint}/atlases/${encodeURIComponent(atlasId)}/parcellations`,
+      { responseType: 'json' }
+    )
+  }
 
-      this.workerService.worker.postMessage({
-        type: 'PROPAGATE_PARC_REGION_ATTR',
-        parcellation,
-        inheritAttrsOpts: {
-          ngId: (parcellation as any ).ngId,
-          relatedAreas: [],
-          fullId: null
-        },
-        id
-      })
+  private httpCallCache = new Map<string, Observable<any>>()
 
-      return this.workerUpdateParcellation$.pipe(
-        filter(({ id: returnedId }) => id === returnedId),
-        take(1),
-        map(({ parcellation }) => parcellation)
-      )
-    })
-  )
+  private getParcDetail(atlasId: string, parcId: string) {
+    return this.http.get<TParc>(
+      `${this.bsEndpoint}/atlases/${encodeURIComponent(atlasId)}/parcellations/${encodeURIComponent(parcId)}`,
+      { responseType: 'json' }
+    )
+  }
 
-  public getTemplateEndpoint$ = this.http.get<any[]>(`${this.backendUrl}templates`, { responseType: 'json' }).pipe(
-    catchError(() => {
-      this.log.warn(`fetching root /tempaltes error`)
-      return of([])
-    }),
-    shareReplay(),
-  )
+  private getSpaces(atlasId: string){
+    return this.http.get<TSpaceSummary[]>(
+      `${this.bsEndpoint}/atlases/${encodeURIComponent(atlasId)}/spaces`,
+      { responseType: 'json' }
+    )
+  }
+
+  private getSpaceDetail(atlasId: string, spaceId: string) {
+    return this.http.get<TSpaceFull>(
+      `${this.bsEndpoint}/atlases/${encodeURIComponent(atlasId)}/spaces/${encodeURIComponent(spaceId)}`,
+      { responseType: 'json' }
+    )
+  }
 
-  public initFetchTemplate$ = this.getTemplateEndpoint$.pipe(
-    switchMap((templates: string[]) => merge(
-      ...templates.map(templateName => this.fetchTemplate(templateName).pipe(
-        switchMap(template => this.processTemplate(template).pipe(
-          map(parcellations => {
-            return {
-              ...template,
-              parcellations
+  private getSpacesAndParc(atlasId: string): Observable<{ templateSpaces: TSpaceFull[], parcellations: TParc[] }> {
+    const cacheKey = `getSpacesAndParc::${atlasId}`
+    if (this.httpCallCache.has(cacheKey)) return this.httpCallCache.get(cacheKey)
+    
+    const spaces$ = this.getSpaces(atlasId).pipe(
+      switchMap(spaces => spaces.length > 0
+        ? forkJoin(
+          spaces.map(space => this.getSpaceDetail(atlasId, parseId(space.id)))
+        )
+        : of([]))
+    )
+    const parcs$ = this.getParcs(atlasId).pipe(
+      // need not to get full parc data. first level gets all data
+      // switchMap(parcs => forkJoin(
+      //   parcs.map(parc => this.getParcDetail(atlasId, parseId(parc.id)))
+      // ))
+    )
+    const returnObs = forkJoin([
+      spaces$,
+      parcs$,
+    ]).pipe(
+      map(([ templateSpaces, parcellations ]) => {
+        /**
+         * select only parcellations that contain renderable volume(s)
+         */
+        const filteredParcellations = parcellations.filter(p => {
+          for (const spaceKey in p.volumeSrc) {
+            for (const hemisphereKey in p.volumeSrc[spaceKey]) {
+              if (p.volumeSrc[spaceKey][hemisphereKey].some(vol => vol.volume_type === 'neuroglancer/precomputed')) return true
+              if (p.volumeSrc[spaceKey][hemisphereKey].some(vol => vol.volume_type === 'neuroglancer/precompmesh')) return true
+              if (p.volumeSrc[spaceKey][hemisphereKey].some(vol => vol.volume_type === 'threesurfer/gii')) return true
+              if (p.volumeSrc[spaceKey][hemisphereKey].some(vol => vol.volume_type === 'threesurfer/gii-label')) return true
             }
-          })
-        ))
-      )),
-    )),
-    catchError((err) => {
-      this.log.warn(`fetching templates error`, err)
-      return of(null)
-    }),
-  )
+          }
+          return false
+        })
+
+        /**
+         * remove parcellation versions that are marked as deprecated
+         * and assign prev/next id accordingly
+         */
+        for (const p of filteredParcellations) {
+          if (!p.version) continue
+          if (p.version.deprecated) {
+            const prevId = p.version.prev
+            const nextId = p.version.next
+
+            const prev = prevId && filteredParcellations.find(p => parseId(p.id) === prevId)
+            const next = nextId && filteredParcellations.find(p => parseId(p.id) === nextId)
+
+            const newPrevId = prev && parseId(prev.id)
+            const newNextId = next && parseId(next.id)
+
+            if (!!prev.version) {
+              prev.version.next = newNextId
+            }
+
+            if (!!next.version) {
+              next.version.prev = newPrevId
+            }
+          }
+        }
+        const removeDeprecatedParc = filteredParcellations.filter(p => {
+          if (!p.version) return true
+          return !(p.version.deprecated)
+        })
+
+        return {
+          templateSpaces,
+          parcellations: removeDeprecatedParc
+        }
+      }),
+      shareReplay(1)
+    )
+    this.httpCallCache.set(cacheKey, returnObs)
+    return returnObs
+  }
 
   constructor(
     private store: Store<any>,
     private http: HttpClient,
     private log: LoggingService,
     private workerService: AtlasWorkerService,
+    @Inject(BS_ENDPOINT) private bsEndpoint: string,
   ){
     this.darktheme$ = this.store.pipe(
-      select(state => state?.viewerState?.templateSelected?.useTheme === 'dark')
+      select(viewerStateSelectedTemplateSelector),
+      map(tmpl => tmpl?.useTheme === 'dark')
     )
 
     this.useTouchUI$ = this.store.pipe(
@@ -112,21 +343,6 @@ export class PureContantService implements OnDestroy{
       shareReplay(1)
     )
 
-    this.fetchedAtlases$ = this.http.get(`${BACKENDURL.replace(/\/$/, '')}/atlases/`, { responseType: 'json' }).pipe(
-      catchError((err, obs) => of(null)),
-      filter(v => !!v),
-      tap((arr: any[]) => this.totalAtlasesLength = arr.length),
-      switchMap(atlases => merge(
-        ...atlases.map(({ url }) => this.http.get(
-          /^http/.test(url)
-            ? url
-            : `${BACKENDURL.replace(/\/$/, '')}/${url}`,
-          { responseType: 'json' }))
-      )),
-      scan((acc, curr) => acc.concat(curr).sort((a, b) => (a.order || 1000) - (b.order || 1001)), []),
-      shareReplay(1)
-    )
-
     this.subscriptions.push(
       this.fetchedAtlases$.subscribe(fetchedAtlases => 
         this.store.dispatch(
@@ -134,9 +350,377 @@ export class PureContantService implements OnDestroy{
         )
       )
     )
+
+    this.allFetchingReady$ = combineLatest([
+      this.initFetchTemplate$.pipe(
+        filter(v => !!v),
+        map(arr => arr.length),
+      ),
+      this.store.pipe(
+        select(viewerStateFetchedTemplatesSelector),
+        map(arr => arr.length),
+      ),
+      this.store.pipe(
+        select(viewerStateFetchedAtlasesSelector),
+        map(arr => arr.length),
+      )
+    ]).pipe(
+      map(([ expNumTmpl, actNumTmpl, actNumAtlas ]) => {
+        return expNumTmpl === actNumTmpl && actNumAtlas === this.totalAtlasesLength
+      }),
+      distinctUntilChanged(),
+      shareReplay(1),
+    )
   }
 
-  private subscriptions: Subscription[] = []
+  private getAtlases$ = this.http.get<TAtlas[]>(
+    `${this.bsEndpoint}/atlases`,
+    {
+      responseType: 'json'
+    }
+  ).pipe(
+    shareReplay(1)
+  )
+
+  public fetchedAtlases$: Observable<TIAVAtlas[]> = this.getAtlases$.pipe(
+    switchMap(atlases => {
+      return forkJoin(
+        atlases.map(
+          atlas => this.getSpacesAndParc(atlas.id).pipe(
+            map(({ templateSpaces, parcellations }) => {
+              return {
+                '@id': atlas.id,
+                name: atlas.name,
+                templateSpaces: templateSpaces.map(tmpl => {
+                  return {
+                    '@id': tmpl.id,
+                    name: tmpl.name,
+                    availableIn: tmpl.availableParcellations.map(parc => {
+                      return {
+                        '@id': parc.id,
+                        name: parc.name
+                      }
+                    }),
+                    originDatainfos: tmpl.originDatainfos || []
+                  }
+                }),
+                parcellations: parcellations.filter(p => {
+                  if (p.version?.deprecated) return false
+                  return true
+                }).map(parc => {
+                  return {
+                    '@id': parseId(parc.id),
+                    name: parc.name,
+                    baseLayer: parc.modality === 'cytoarchitecture',
+                    '@version': {
+                      '@next': parc.version?.next,
+                      '@previous': parc.version?.prev,
+                      'name': parc.version?.name,
+                      '@this': parseId(parc.id)
+                    },
+                    groupName: parc.modality || null,
+                    availableIn: parc.availableSpaces.map(space => {
+                      return {
+                        '@id': space.id,
+                        name: space.name,
+                        /**
+                         * TODO need original data format
+                         */
+                        // originalDatasetFormats: [{
+                        //   name: "probability map"
+                        // }]
+                      }
+                    }),
+                    originDatainfos: parc.originDatainfos || []
+                  }
+                })
+              }
+            }),
+            catchError((err, obs) => {
+              console.error(err)
+              return of(null)
+            })
+          )
+        )
+      )
+    }),
+    catchError((err, obs) => of([])),
+    tap((arr: any[]) => this.totalAtlasesLength = arr.length),
+    scan((acc, curr) => acc.concat(curr).sort((a, b) => (a.order || 1001) - (b.order || 1000)), []),
+    shareReplay(1)
+  )
+
+  public initFetchTemplate$ = this.fetchedAtlases$.pipe(
+    switchMap(atlases => {
+      return forkJoin(
+        atlases.map(atlas => this.getSpacesAndParc(atlas['@id']).pipe(
+          switchMap(({ templateSpaces, parcellations }) => {
+            const ngLayerObj = {}
+            return forkJoin(
+              templateSpaces.map(
+                tmpl => {
+                  ngLayerObj[tmpl.id] = {}
+                  return tmpl.availableParcellations.map(
+                    parc => this.getRegions(atlas['@id'], parc.id, tmpl.id).pipe(
+                      tap(regions => {
+                        recursiveMutate(
+                          regions,
+                          region => region.children,
+                          region => {
+                            /**
+                             * individual map(s)
+                             * this should work for both fully mapped and interpolated
+                             * in the case of interpolated, it sucks that the ngLayerObj will be set multiple times
+                             */
+                            if (
+                              tmpl.id in (region.volumeSrc || {})
+                              && 'collect' in region.volumeSrc[tmpl.id]
+                            ) {
+                              const dedicatedMap = region.volumeSrc[tmpl.id]['collect'].filter(v => v.volume_type === 'neuroglancer/precomputed')
+                              if (dedicatedMap.length === 1) {
+                                const ngId = getNgId(atlas['@id'], tmpl.id, parc.id, dedicatedMap[0]['@id'])
+                                region['ngId'] = ngId
+                                region['labelIndex'] = dedicatedMap[0].detail['neuroglancer/precomputed'].labelIndex
+                                ngLayerObj[tmpl.id][ngId] = {
+                                  source: `precomputed://${dedicatedMap[0].url}`,
+                                  type: "segmentation",
+                                  transform: dedicatedMap[0].detail['neuroglancer/precomputed'].transform
+                                }
+                              }
+                            }
+  
+                            /**
+                             * if label index is defined
+                             */
+                            if (!!region.labelIndex) {
+                              const hemisphereKey = /left hemisphere|left/.test(region.name)
+                                // these two keys are, unfortunately, more or less hardcoded
+                                // which is less than ideal
+                                ? 'left hemisphere'
+                                : /right hemisphere|right/.test(region.name)
+                                  ? 'right hemisphere'
+                                  : 'whole brain'
+
+                              /**
+                               * TODO fix in siibra-api
+                               */
+                              if (
+                                tmpl.id !== 'minds/core/referencespace/v1.0.0/a1655b99-82f1-420f-a3c2-fe80fd4c8588'
+                                && parc.id === 'minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-290'
+                                && hemisphereKey === 'whole brain'
+                              ) {
+                                region.labelIndex = null
+                                return
+                              }
+
+                              if (
+                                tmpl.id === 'minds/core/referencespace/v1.0.0/a1655b99-82f1-420f-a3c2-fe80fd4c8588'
+                                && parc.id === 'minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-290'
+                                && hemisphereKey === 'whole brain'
+                              ) {
+                                region.children = []
+                                return
+                              }
+
+                              const hemispheredNgId = getNgId(atlas['@id'], tmpl.id, parc.id, hemisphereKey)
+                              region['ngId'] = hemispheredNgId
+                            }
+                          }  
+                        )
+                        this.atlasParcSpcRegionMap.set(
+                          atlas['@id'], tmpl.id, parc.id, regions
+                        )
+  
+                        /**
+                         * populate maps for parc
+                         */
+                        for (const parc of parcellations) {
+                          if (tmpl.id in (parc.volumeSrc || {})) {
+                            // key: 'left hemisphere' | 'right hemisphere' | 'whole brain'
+                            for (const key in (parc.volumeSrc[tmpl.id] || {})) {
+                              for (const vol of parc.volumeSrc[tmpl.id][key]) {
+                                if (vol.volume_type === 'neuroglancer/precomputed') {
+                                  const ngIdKey = getNgId(atlas['@id'], tmpl.id, parseId(parc.id), key)
+                                  ngLayerObj[tmpl.id][ngIdKey] = {
+                                    source: `precomputed://${vol.url}`,
+                                    type: "segmentation",
+                                    transform: vol.detail['neuroglancer/precomputed'].transform
+                                  }
+                                }
+                              }
+                            }
+                          }
+                        }
+                      }),
+                      catchError((err, obs) => {
+                        return of(null)
+                      })
+                    )
+                  )
+                }
+              ).reduce(flattenReducer, [])
+            ).pipe(
+              mapTo({ templateSpaces, parcellations, ngLayerObj })
+            )
+          }),
+          map(({ templateSpaces, parcellations, ngLayerObj }) => {
+            return templateSpaces.map(tmpl => {
+
+              // configuring three-surfer
+              let threeSurferConfig = {}
+              const threeSurferVolSrc = tmpl.volume_src.find(v => v.volume_type === 'threesurfer/gii')
+              if (threeSurferVolSrc) {
+                const foundP = parcellations.find(p => {
+                  return !!p.volumeSrc[tmpl.id]
+                })
+                const url = threeSurferVolSrc.url
+                const { surfaces } = threeSurferVolSrc.detail['threesurfer/gii'] as { surfaces: {mode: string, hemisphere: 'left' | 'right', url: string}[] }
+                const modObj = {}
+                for (const surface of surfaces) {
+                  
+                  const hemisphereKey = surface.hemisphere === 'left'
+                    ? 'left hemisphere'
+                    : 'right hemisphere'
+
+
+                  /**
+                   * concating all available gii maps
+                   */
+                  // const allFreesurferLabels = foundP.volumeSrc[tmpl.id][hemisphereKey].filter(v => v.volume_type === 'threesurfer/gii-label')
+                  // for (const lbl of allFreesurferLabels) {
+                  //   const modeToConcat = {
+                  //     mesh: surface.url,
+                  //     hemisphere: surface.hemisphere,
+                  //     colormap: lbl.url
+                  //   }
+
+                  //   const key = `${surface.mode} - ${lbl.name}`
+                  //   if (!modObj[key]) {
+                  //     modObj[key] = []
+                  //   }
+                  //   modObj[key].push(modeToConcat)
+                  // }
+
+                  /**
+                   * only concat first matching gii map
+                   */
+                  const key = surface.mode
+                  const modeToConcat = {
+                    mesh: surface.url,
+                    hemisphere: surface.hemisphere,
+                    colormap: (() => {
+                      const lbl = foundP.volumeSrc[tmpl.id][hemisphereKey].find(v => v.volume_type === 'threesurfer/gii-label')
+                      return lbl?.url
+                    })()
+                  }
+                  if (!modObj[key]) {
+                    modObj[key] = []
+                  }
+                  modObj[key].push(modeToConcat)
+
+                }
+                foundP[tmpl.id]
+                threeSurferConfig = {
+                  "three-surfer": {
+                    '@context': {
+                      root: url
+                    },
+                    modes: Object.keys(modObj).map(name => {
+                      return {
+                        name,
+                        meshes: modObj[name]
+                      }
+                    })
+                  },
+                  nehubaConfig: null,
+                  nehubaConfigURL: null,
+                  useTheme: 'dark'
+                }
+              }
+              const darkTheme = tmpl.src_volume_type === 'mri'
+              const nehubaConfig = getNehubaConfig(tmpl)
+              const initialLayers = nehubaConfig.dataset.initialNgState.layers
+              
+              const tmplNgId = tmpl.name
+              const tmplAuxMesh = `${tmpl.name} auxmesh`
+
+              const precomputed = tmpl.volume_src.find(src => src.volume_type === 'neuroglancer/precomputed')
+              if (precomputed) {
+                initialLayers[tmplNgId] = {
+                  type: "image",
+                  source: `precomputed://${precomputed.url}`,
+                  transform: precomputed.detail['neuroglancer/precomputed'].transform
+                }
+              }
+
+              // TODO
+              // siibra-python accidentally left out volume type of precompmesh
+              // https://github.com/FZJ-INM1-BDA/siibra-python/pull/55
+              // use url to determine for now
+              // const precompmesh = tmpl.volume_src.find(src => src.volume_type === 'neuroglancer/precompmesh')
+              const precompmesh = tmpl.volume_src.find(src => !!src.detail?.['neuroglancer/precompmesh'])
+              const auxMeshes = []
+              if (precompmesh){
+                initialLayers[tmplAuxMesh] = {
+                  source: `precompmesh://${precompmesh.url}`,
+                  type: "segmentation",
+                  transform: precompmesh.detail['neuroglancer/precompmesh'].transform
+                }
+                for (const auxMesh of precompmesh.detail['neuroglancer/precompmesh'].auxMeshes) {
+
+                  auxMeshes.push({
+                    ...auxMesh,
+                    ngId: tmplAuxMesh,
+                    '@id': `${tmplAuxMesh} ${auxMesh.name}`,
+                    visible: true
+                  })
+                }
+              }
+
+              for (const key in (ngLayerObj[tmpl.id] || {})) {
+                initialLayers[key] = ngLayerObj[tmpl.id][key]
+              }
+
+              return {
+                name: tmpl.name,
+                '@id': tmpl.id,
+                fullId: tmpl.id,
+                useTheme: darkTheme ? 'dark' : 'light',
+                ngId: tmplNgId,
+                nehubaConfig,
+                auxMeshes,
+                /**
+                 * only populate the parcelltions made available
+                 */
+                parcellations: tmpl.availableParcellations.filter(
+                  p => parcellations.some(p2 => parseId(p2.id) === p.id)
+                ).map(parc => {
+                  const fullParcInfo = parcellations.find(p => parseId(p.id) === parc.id)
+                  const regions = this.atlasParcSpcRegionMap.get(atlas['@id'], tmpl.id, parc.id) || []
+                  return {
+                    fullId: parc.id,
+                    '@id': parc.id,
+                    name: parc.name,
+                    regions,
+                    originDatainfos: fullParcInfo?.originDatainfos || []
+                  }
+                }),
+                ...threeSurferConfig
+              }
+            })
+          })
+        ))
+      )
+    }),
+    map(arr => {
+      return arr.reduce(flattenReducer, [])
+    }),
+    catchError((err) => {
+      this.log.warn(`fetching templates error`, err)
+      return of(null)
+    }),
+    shareReplay(1),
+  )
 
   ngOnDestroy(){
     while(this.subscriptions.length > 0) this.subscriptions.pop().unsubscribe()
diff --git a/src/util/regDereg.base.spec.ts b/src/util/regDereg.base.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..abb5d3e627723a0b54b7cc95136b6334ae791a9b
--- /dev/null
+++ b/src/util/regDereg.base.spec.ts
@@ -0,0 +1,81 @@
+import { RegDereg } from "./regDereg.base"
+
+describe('> regDereg.base.ts', () => {
+  describe('> RegDereg', () => {
+    let regDereg: RegDereg<(arg: string) => boolean>
+    beforeEach(() => {
+      regDereg = new RegDereg()
+    })
+
+    describe('> #register', () => {
+      it('> adds interceptor fn', () => {
+        let nextReturnVal = false
+        const fn = (ev: any) => nextReturnVal
+        regDereg.register(fn)
+        expect(regDereg['callbacks'].indexOf(fn)).toBeGreaterThanOrEqual(0)
+      })
+      it('> when config not supplied, or first not present, will add fn to the last of the queue', () => {
+        let dummyReturn = false
+        const dummy = (ev: any) => dummyReturn
+        regDereg.register(dummy)
+        
+        let fnReturn = false
+        const fn = (ev: any) => fnReturn
+        regDereg.register(fn)
+        expect(regDereg['callbacks'].indexOf(fn)).toEqual(1)
+
+        let fn2Return = false
+        const fn2 = (ev: any) => fn2Return
+        regDereg.register(fn2, {})
+        expect(regDereg['callbacks'].indexOf(fn)).toEqual(1)
+        expect(regDereg['callbacks'].indexOf(fn2)).toEqual(2)
+      })
+      it('> when first is supplied as a config param, will add the fn at the front', () => {
+
+        let dummyReturn = false
+        const dummy = (ev: any) => dummyReturn
+        regDereg.register(dummy)
+
+        let fnReturn = false
+        const fn = (ev: any) => fnReturn
+        regDereg.register(fn, {
+          first: true
+        })
+        expect(regDereg['callbacks'].indexOf(fn)).toEqual(0)
+
+      })
+    })
+
+    describe('> deregister', () => {
+      it('> if the fn exist in the register, it will be removed', () => {
+
+        let fnReturn = false
+        let fn2Return = false
+        const fn = (ev: any) => fnReturn
+        const fn2 = (ev: any) => fn2Return
+        regDereg.register(fn)
+        expect(regDereg['callbacks'].indexOf(fn)).toBeGreaterThanOrEqual(0)
+        expect(regDereg['callbacks'].length).toEqual(1)
+
+        regDereg.deregister(fn)
+        expect(regDereg['callbacks'].indexOf(fn)).toBeLessThan(0)
+        expect(regDereg['callbacks'].length).toEqual(0)
+      })
+
+      it('> if fn does not exist in register, it will not be removed', () => {
+        
+        let fnReturn = false
+        let fn2Return = false
+        const fn = (ev: any) => fnReturn
+        const fn2 = (ev: any) => fn2Return
+        regDereg.register(fn)
+        expect(regDereg['callbacks'].indexOf(fn)).toBeGreaterThanOrEqual(0)
+        expect(regDereg['callbacks'].length).toEqual(1)
+
+        regDereg.deregister(fn2)
+        expect(regDereg['callbacks'].indexOf(fn)).toBeGreaterThanOrEqual(0)
+        expect(regDereg['callbacks'].length).toEqual(1)
+      })
+    })
+  })
+})
diff --git a/src/util/regDereg.base.ts b/src/util/regDereg.base.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2d135f130a661b58771f208cee61e2c97eff3b26
--- /dev/null
+++ b/src/util/regDereg.base.ts
@@ -0,0 +1,46 @@
+type TRegDeregConfig = {
+  first?: boolean
+}
+
+/**
+ * this is base register/dregister class
+ * a pattern which is observed very frequently
+ * e.g. click interceptor, context menu interceptor
+ */
+export class RegDereg<T> {
+
+  // eslint-disable-next-line @typescript-eslint/no-empty-function
+  constructor(){}
+  public allowDuplicate = false
+  protected callbacks: T[] = []
+  register(fn: T, config?: TRegDeregConfig) {
+    if (!this.allowDuplicate) {
+      if (this.callbacks.indexOf(fn) >= 0) {
+        console.warn(`[RegDereg] #register: function has already been regsitered`)
+        return
+      }
+    }
+    if (config?.first) {
+      this.callbacks.unshift(fn)
+    } else {
+      this.callbacks.push(fn)
+    }
+  }
+  deregister(fn: T){
+    this.callbacks = this.callbacks.filter(f => f !== fn )
+  }
+}
+
+export class RegDeregController<T, Y = void> extends RegDereg<(arg: T) => Y>{
+  constructor(){
+    super()
+  }
+  /**
+   * Can be overwritten by inherited class
+   */
+  callRegFns(arg: T) {
+    for (const fn of this.callbacks) {
+      fn(arg)
+    }
+  }
+}
diff --git a/src/util/siibraApiConstants/fn.ts b/src/util/siibraApiConstants/fn.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cf820dcf68bbe99dea6d136a2b78f076a5aa5e19
--- /dev/null
+++ b/src/util/siibraApiConstants/fn.ts
@@ -0,0 +1,6 @@
+import { TRegionDetail } from "./types";
+
+export function getPosFromRegion(region: TRegionDetail){
+  if (!region?.props?.[0]) return null
+  return region.props[0].centroid_mm.map(v => v*1e6) as [number, number, number]
+}
\ No newline at end of file
diff --git a/src/util/siibraApiConstants/types.ts b/src/util/siibraApiConstants/types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..da56c848a3e80b39b6f709373096be1e27e297be
--- /dev/null
+++ b/src/util/siibraApiConstants/types.ts
@@ -0,0 +1,182 @@
+type THref = {
+  href: string
+}
+
+type TSpaceType = 'mri' | 'histology'
+
+type TNgTransform = number[][]
+
+type TVolumeType = 'nii' | 'neuroglancer/precomputed' | 'neuroglancer/precompmesh' | 'detailed maps' | 'threesurfer/gii' | 'threesurfer/gii-label'
+type TParcModality = 'cytoarchitecture' | 'functional modes' | 'fibre architecture'
+
+type TAuxMesh = {
+  name: string
+  labelIndicies: number[]
+}
+
+interface IVolumeTypeDetail {
+  'nii': null
+  'neuroglancer/precomputed': {
+    'neuroglancer/precomputed': {
+      'transform': TNgTransform
+    }
+  }
+  'neuroglancer/precompmesh': {
+    'neuroglancer/precompmesh': {
+      'auxMeshes': TAuxMesh[]
+      'transform': TNgTransform
+    }
+  }
+  'detailed maps': null
+}
+
+type TVolumeSrc<VolumeType extends keyof IVolumeTypeDetail> = {
+  '@id': string
+  '@type': 'fzj/tmp/volume_type/v0.0.1'
+  name: string
+  url: string
+  volume_type: TVolumeType
+  detail: IVolumeTypeDetail[VolumeType]
+}
+
+type TKgIdentifier = {
+  kgSchema: string
+  kgId: string
+}
+
+type TVersion = {
+  name: string
+  prev: string | null
+  next: string | null
+  deprecated?: boolean
+}
+
+export type TId = string | { kg: TKgIdentifier }
+
+export type TAtlas = {
+  id: string
+  name: string
+  links: {
+    parcellations: THref
+    spaces: THref
+  }
+}
+
+export type TSpaceSummary = {
+  id: {
+    kg: TKgIdentifier
+  }
+  name: string
+  links: {
+    self: THref
+  }
+}
+
+export type TParcSummary = {
+  id: string
+  name: string
+}
+
+export type TDatainfos = {
+  name: string
+  description: string
+  urls: {
+    cite: string
+    doi: string
+  }[]
+  useClassicUi: boolean
+}
+
+export type TSpaceFull = {
+  id: string
+  name: string
+  key: string //???
+  type: string //???
+  url: string //???
+  ziptarget: string //???
+  src_volume_type: TSpaceType
+  volume_src: TVolumeSrc<keyof IVolumeTypeDetail>[]
+  availableParcellations: TParcSummary[]
+  links: {
+    templates: THref
+    parcellation_maps: THref
+  }
+  originDatainfos: TDatainfos[]
+}
+
+export type TParc = {
+  id: {
+    kg: TKgIdentifier
+  }
+  name: string
+  availableSpaces: {
+    id: string
+    name: string
+  }[]
+  links: {
+    self: THref
+  }
+  regions: THref
+  modality: TParcModality
+  version: TVersion
+  volumeSrc: {
+    [key: string]: {
+      [key: string]: TVolumeSrc<keyof IVolumeTypeDetail>[]
+    }
+  }
+  originDatainfos: TDatainfos[]
+}
+
+export type TRegionDetail = {
+  name: string
+  children: TRegionDetail[]
+  rgb: number[]
+  id: string
+  labelIndex: number
+  volumeSrc: {
+    [key: string]: {
+      [key: string]: TVolumeSrc<keyof IVolumeTypeDetail>[]
+    }
+  }
+  availableIn: {
+    id: string
+    name: string
+  }[]
+  hasRegionalMap: boolean
+  props: {
+    centroid_mm: [number, number, number]
+    volume_mm: number
+    surface_mm: number
+    is_cortical: number
+  }[]
+  links: {
+    [key: string]: string
+  }
+  originDatainfos: TDatainfos[]
+}
+
+export type TRegion = {
+  name: string
+  children: TRegion[]
+  volumeSrc: {
+    [key: string]: {
+      [key: string]: TVolumeSrc<keyof IVolumeTypeDetail>[]
+    }
+  }
+
+  labelIndex?: number
+  rgb?: number[]
+  id?: {
+    kg: TKgIdentifier
+  }
+
+  /**
+   * missing 
+   */
+
+  originDatasets?: ({
+    filename: string
+  } & TKgIdentifier) []
+
+  position?: number[]
+}
diff --git a/src/util/util.module.ts b/src/util/util.module.ts
index 8d45bb6c946b6cae12a3888f95f4ee947f5159e5..51b202ea5664691f4aa242bdfeed59516d6f4b6b 100644
--- a/src/util/util.module.ts
+++ b/src/util/util.module.ts
@@ -25,6 +25,8 @@ import { FilterByPropertyPipe } from "./pipes/filterByProperty.pipe";
 import { ArrayContainsPipe } from "./pipes/arrayContains.pipe";
 import { DoiParserPipe } from "./pipes/doiPipe.pipe";
 import { TmpParcNamePipe } from "./pipes/_tmpParcName.pipe";
+import { ObjectToArrayPipe } from "./pipes/objToArray.pipe";
+import { GetFilenamePipe } from "./pipes/getFilename.pipe";
 
 @NgModule({
   imports:[
@@ -55,6 +57,8 @@ import { TmpParcNamePipe } from "./pipes/_tmpParcName.pipe";
     ArrayContainsPipe,
     DoiParserPipe,
     TmpParcNamePipe,
+    ObjectToArrayPipe,
+    GetFilenamePipe,
   ],
   exports: [
     FilterNullPipe,
@@ -81,6 +85,8 @@ import { TmpParcNamePipe } from "./pipes/_tmpParcName.pipe";
     ArrayContainsPipe,
     DoiParserPipe,
     TmpParcNamePipe,
+    ObjectToArrayPipe,
+    GetFilenamePipe,
   ]
 })
 
diff --git a/src/util/windowResize/index.ts b/src/util/windowResize/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cd22fb2f4a0c9a9fa9687f14ebe94cfbf5b0f671
--- /dev/null
+++ b/src/util/windowResize/index.ts
@@ -0,0 +1,2 @@
+export { WindowResizeModule } from './module'
+export { ResizeObserverDirective } from './windowResize.directive'
diff --git a/src/util/windowResize/module.ts b/src/util/windowResize/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..61392d6f4ea9770288d253264095ad63cff1d96c
--- /dev/null
+++ b/src/util/windowResize/module.ts
@@ -0,0 +1,17 @@
+import { NgModule } from "@angular/core";
+import { ResizeObserverDirective } from "./windowResize.directive";
+import { ResizeObserverService } from "./windowResize.service";
+
+@NgModule({
+  declarations: [
+    ResizeObserverDirective
+  ],
+  exports: [
+    ResizeObserverDirective
+  ],
+  providers: [
+    ResizeObserverService
+  ]
+})
+
+export class WindowResizeModule{}
diff --git a/src/util/windowResize/windowResize.directive.ts b/src/util/windowResize/windowResize.directive.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ce8de31804fcaec53296925c5eef5235979719b0
--- /dev/null
+++ b/src/util/windowResize/windowResize.directive.ts
@@ -0,0 +1,64 @@
+import { Directive, EventEmitter, Input, OnChanges, OnInit, Output } from "@angular/core";
+import { Subscription } from "rxjs";
+import { ResizeObserverService } from "./windowResize.service";
+
+@Directive({
+  selector: '[iav-window-resize]',
+  exportAs: 'iavWindowResize'
+})
+
+export class ResizeObserverDirective implements OnChanges, OnInit {
+  @Input('iav-window-resize-type')
+  type: 'debounce' | 'throttle' = 'throttle'
+
+  @Input('iav-window-resize-time')
+  time: number = 160
+
+  @Input('iav-window-resize-throttle-leading')
+  throttleLeading = false
+
+  @Input('iav-window-resize-throttle-trailing')
+  throttleTrailing = true
+
+  @Output('iav-window-resize-event')
+  ev: EventEmitter<Event> = new EventEmitter()
+
+  private sub: Subscription[] = []
+
+  constructor(private svc: ResizeObserverService){}
+
+  ngOnInit(){
+    this.configure()
+  }
+  ngOnChanges(){
+    this.configure()
+  }
+
+  configure(){
+    while(this.sub.length > 0) this.sub.pop().unsubscribe()
+
+    let sub: Subscription
+    if (this.type === 'throttle') {
+      sub = this.svc.getThrottledResize(
+        this.time, 
+        {
+          leading: this.throttleLeading,
+          trailing: this.throttleTrailing
+        }
+      ).subscribe(event => this.ev.emit(event))
+    }
+
+    if (this.type === 'debounce') {
+      sub = this.svc.getDebouncedResize(
+        this.time,
+      ).subscribe(event => this.ev.emit(event))
+    }
+
+    if (!this.type) {
+      sub = this.svc.windowResize.pipe(
+      ).subscribe(event => this.ev.emit(event))
+    }
+
+    this.sub.push(sub)
+  }
+}
diff --git a/src/util/windowResize/windowResize.service.ts b/src/util/windowResize/windowResize.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f50c7df0e929a935b56176311b887d585d6412eb
--- /dev/null
+++ b/src/util/windowResize/windowResize.service.ts
@@ -0,0 +1,30 @@
+import { Injectable } from "@angular/core";
+import { asyncScheduler, fromEvent } from "rxjs";
+import { debounceTime, shareReplay, tap, throttleTime } from "rxjs/operators";
+
+interface IThrottleConfig {
+  leading: boolean
+  trailing: boolean
+}
+
+@Injectable({
+  providedIn: 'root'
+})
+
+export class ResizeObserverService {
+  public windowResize = fromEvent(window, 'resize').pipe(
+    shareReplay(1)
+  )
+
+  public getThrottledResize(time: number, config?: IThrottleConfig){
+    return this.windowResize.pipe(
+      throttleTime(time, asyncScheduler, config || { leading: false, trailing: true }),
+    )
+  }
+
+  public getDebouncedResize(time: number) {
+    return this.windowResize.pipe(
+      debounceTime(time)
+    )
+  }
+}
diff --git a/src/viewerCtrl/index.ts b/src/viewerCtrl/index.ts
deleted file mode 100644
index 32598a8ce9159499453bb4e35ae719bb7448e983..0000000000000000000000000000000000000000
--- a/src/viewerCtrl/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { ViewerCtrlModule } from './module'
\ No newline at end of file
diff --git a/src/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.component.ts b/src/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.component.ts
deleted file mode 100644
index 93e6f3def9078f23195e9bea73dac0165214c17f..0000000000000000000000000000000000000000
--- a/src/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.component.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-import { Component, HostBinding } from "@angular/core";
-import { select, Store } from "@ngrx/store";
-import { Subscription } from "rxjs";
-import { viewerStateSelectedTemplatePureSelector } from "src/services/state/viewerState/selectors";
-
-
-@Component({
-  selector: 'viewer-ctrl-component',
-  templateUrl: './viewerCtrlCmp.template.html',
-  styleUrls: [
-    './viewerCtrlCmp.style.css'
-  ],
-  exportAs: 'viewerCtrlCmp'
-})
-
-export class ViewerCtrlCmp{
-
-  @HostBinding('attr.darktheme')
-  darktheme = false
-
-  private sub: Subscription[] = []
-  private hiddenLayerNames: string[] = []
-
-  constructor(
-    private store$: Store<any>
-  ){
-
-    this.sub.push(
-      this.store$.pipe(
-        select(viewerStateSelectedTemplatePureSelector)
-      ).subscribe(tmpl => {
-        const { useTheme } = tmpl || {}
-        this.darktheme = useTheme === 'dark'
-      })
-    )
-
-  }
-
-  public toggleParcVsbl(){
-    const visibleParcLayers = ((window as any).viewer.layerManager.managedLayers)
-      .slice(1)
-      .filter(({ visible }) => visible)
-
-    const allParcHidden = visibleParcLayers.length === 0
-    
-    if (allParcHidden) {
-      for (const name of this.hiddenLayerNames) {
-        const l = (window as any).viewer.layerManager.getLayerByName(name)
-        l && l.setVisible(true)
-      }
-      this.hiddenLayerNames = []
-    } else {
-      this.hiddenLayerNames = []
-      for (const { name } of visibleParcLayers) {
-        const l = (window as any).viewer.layerManager.getLayerByName(name)
-        l && l.setVisible(false)
-        this.hiddenLayerNames.push( name )
-      }
-    }
-    
-    setTimeout(() => {
-      (window as any).viewer.display.scheduleRedraw()
-    })
-  }
-}
\ No newline at end of file
diff --git a/src/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.template.html b/src/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.template.html
deleted file mode 100644
index 2ba8e30622edf364319af102febfd0fe26d6d30c..0000000000000000000000000000000000000000
--- a/src/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.template.html
+++ /dev/null
@@ -1,14 +0,0 @@
-<h3 class="iv-custom-comp text mat-title">
-  Parcellations
-</h3>
-
-<mat-divider></mat-divider>
-
-<button mat-flat-button color="warn"
-  class="mt-2"
-  (click)="toggleParcVsbl()">
-  <i class="fas fa-eye-slash"></i>
-  <span>
-    Clear All
-  </span>
-</button>
\ No newline at end of file
diff --git a/src/viewerModule/componentStore.ts b/src/viewerModule/componentStore.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5fe24c51085c329be8ed3eaee2048f3114cbda43
--- /dev/null
+++ b/src/viewerModule/componentStore.ts
@@ -0,0 +1,36 @@
+import { Injectable } from "@angular/core";
+import { select } from "@ngrx/store";
+import { ReplaySubject, Subject } from "rxjs";
+import { shareReplay } from "rxjs/operators";
+
+export class LockError extends Error{}
+
+/**
+ * polyfill for ngrx component store
+ * until upgrade to v11
+ * where component store becomes generally available
+ */
+
+@Injectable()
+export class ComponentStore<T>{
+  private _state$: Subject<T> = new ReplaySubject<T>(1)
+  private _lock: boolean = false
+  get isLocked() {
+    return this._lock
+  }
+  setState(state: T){
+    if (this.isLocked) throw new LockError('State is locked')
+    this._state$.next(state)
+  }
+  select<V>(selectorFn: (state: T) => V) {
+    return this._state$.pipe(
+      select(selectorFn),
+      shareReplay(1),
+    )
+  }
+  getLock(): () => void {
+    if (this.isLocked) throw new LockError('Cannot get lock. State is locked')
+    this._lock = true
+    return () => this._lock = false
+  }
+}
diff --git a/src/viewerModule/constants.ts b/src/viewerModule/constants.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8fa2d25231b1435428d4ca4f652617904fcc95bc
--- /dev/null
+++ b/src/viewerModule/constants.ts
@@ -0,0 +1,4 @@
+import { InjectionToken } from "@angular/core";
+import { Observable } from "rxjs";
+
+export const VIEWERMODULE_DARKTHEME = new InjectionToken<Observable<boolean>>('VIEWERMODULE_DARKTHEME')
diff --git a/src/viewerModule/index.ts b/src/viewerModule/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..53f950d5421d8dbf54ce6482c72be6dddfe9c377
--- /dev/null
+++ b/src/viewerModule/index.ts
@@ -0,0 +1,2 @@
+export { ViewerModule } from "./module"
+export { VIEWERMODULE_DARKTHEME } from './constants'
\ No newline at end of file
diff --git a/src/viewerModule/module.ts b/src/viewerModule/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7abfbb1bed3c1eb8135526e51a3ddb2a6f8755e2
--- /dev/null
+++ b/src/viewerModule/module.ts
@@ -0,0 +1,89 @@
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { Observable } from "rxjs";
+import { DatabrowserModule } from "src/atlasComponents/databrowserModule";
+import { AtlasCmpParcellationModule } from "src/atlasComponents/parcellation";
+import { ParcellationRegionModule } from "src/atlasComponents/parcellationRegion";
+import { BSFeatureModule, BS_DARKTHEME,  } from "src/atlasComponents/regionalFeatures/bsFeatures";
+import { SplashUiModule } from "src/atlasComponents/splashScreen";
+import { AtlasCmpUiSelectorsModule } from "src/atlasComponents/uiSelectors";
+import { ComponentsModule } from "src/components";
+import { ContextMenuModule, ContextMenuService, TContextMenuReg } from "src/contextMenuModule";
+import { LayoutModule } from "src/layouts/layout.module";
+import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module";
+import { TopMenuModule } from "src/ui/topMenu/module";
+import { CONTEXT_MENU_ITEM_INJECTOR, TContextMenu, UtilModule } from "src/util";
+import { VIEWERMODULE_DARKTHEME } from "./constants";
+import { NehubaModule, NehubaViewerUnit } from "./nehuba";
+import { ThreeSurferModule } from "./threeSurfer";
+import { ViewerCmp } from "./viewerCmp/viewerCmp.component";
+import {UserAnnotationsModule} from "src/atlasComponents/userAnnotations";
+import {QuickTourModule} from "src/ui/quickTour/module";
+import { INJ_ANNOT_TARGET } from "src/atlasComponents/userAnnotations/tools/type";
+import { NEHUBA_INSTANCE_INJTKN } from "./nehuba/util";
+import { map } from "rxjs/operators";
+import { TContextArg } from "./viewer.interface";
+import { ViewerStateBreadCrumbModule } from "./viewerStateBreadCrumb/module";
+import { KgRegionalFeatureModule } from "src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    NehubaModule,
+    ThreeSurferModule,
+    LayoutModule,
+    DatabrowserModule,
+    AtlasCmpUiSelectorsModule,
+    AngularMaterialModule,
+    SplashUiModule,
+    TopMenuModule,
+    ParcellationRegionModule,
+    UtilModule,
+    AtlasCmpParcellationModule,
+    ComponentsModule,
+    BSFeatureModule,
+    UserAnnotationsModule,
+    QuickTourModule,
+    ContextMenuModule,
+    ViewerStateBreadCrumbModule,
+    KgRegionalFeatureModule,
+  ],
+  declarations: [
+    ViewerCmp,
+  ],
+  providers: [
+    {
+      provide: BS_DARKTHEME,
+      useFactory: (obs$: Observable<boolean>) => obs$,
+      deps: [
+        VIEWERMODULE_DARKTHEME
+      ]
+    },
+    {
+      provide: INJ_ANNOT_TARGET,
+      useFactory: (obs$: Observable<NehubaViewerUnit>) => {
+        return obs$.pipe(
+          map(unit => unit?.elementRef?.nativeElement)
+        )
+      },
+      deps: [
+        NEHUBA_INSTANCE_INJTKN
+      ]
+    },
+    {
+      provide: CONTEXT_MENU_ITEM_INJECTOR,
+      useFactory: (svc: ContextMenuService<TContextArg<'threeSurfer' | 'nehuba'>>) => {
+        return {
+          register: svc.register.bind(svc),
+          deregister: svc.deregister.bind(svc)
+        } as TContextMenu<TContextMenuReg<TContextArg<'nehuba' | 'threeSurfer'>>>
+      },
+      deps: [ ContextMenuService ]
+    },
+  ],
+  exports: [
+    ViewerCmp,
+  ],
+})
+
+export class ViewerModule{}
diff --git a/src/viewerModule/nehuba/constants.ts b/src/viewerModule/nehuba/constants.ts
new file mode 100644
index 0000000000000000000000000000000000000000..bbd36606525015fcd2f8e6315a061ea2a7f56a36
--- /dev/null
+++ b/src/viewerModule/nehuba/constants.ts
@@ -0,0 +1,77 @@
+import { InjectionToken } from '@angular/core'
+import { Observable } from 'rxjs'
+
+export { getNgIds } from 'src/util/fn'
+export const NEHUBA_VIEWER_FEATURE_KEY = 'ngViewerFeature'
+
+export interface INgLayerInterface {
+  name: string // displayName
+  source: string
+  mixability: string // base | mixable | nonmixable
+  annotation?: string //
+  id?: string // unique identifier
+  visible?: boolean
+  shader?: string
+  transform?: any
+}
+
+export interface IRegion {
+  [key: string]: any
+  ngId: string
+  rgb?: [number, number, number]
+}
+
+export function getMultiNgIdsRegionsLabelIndexMap(parcellation: any = {}, inheritAttrsOpt: any = { ngId: 'root' }): Map<string, Map<number, IRegion>> {
+  const map: Map<string, Map<number, any>> = new Map()
+  
+  const inheritAttrs = Object.keys(inheritAttrsOpt)
+  if (inheritAttrs.indexOf('children') >=0 ) throw new Error(`children attr cannot be inherited`)
+
+  const processRegion = (region: any) => {
+    const { ngId: rNgId } = region
+    const labelIndex = Number(region.labelIndex)
+    if (labelIndex && rNgId) {
+      const existingMap = map.get(rNgId)
+      if (!existingMap) {
+        const newMap = new Map()
+        newMap.set(labelIndex, region)
+        map.set(rNgId, newMap)
+      } else {
+        existingMap.set(labelIndex, region)
+      }
+    }
+
+    if (region.children && Array.isArray(region.children)) {
+      for (const r of region.children) {
+        const copiedRegion = { ...r }
+        for (const attr of inheritAttrs){
+          copiedRegion[attr] = copiedRegion[attr] || region[attr] || parcellation[attr]
+        }
+        processRegion(copiedRegion)
+      }
+    }
+  }
+
+  if (!parcellation) throw new Error(`parcellation needs to be defined`)
+  if (!parcellation.regions) throw new Error(`parcellation.regions needs to be defined`)
+  if (!Array.isArray(parcellation.regions)) throw new Error(`parcellation.regions needs to be an array`)
+
+  for (const region of parcellation.regions){
+    const copiedregion = { ...region }
+    for (const attr of inheritAttrs){
+      copiedregion[attr] = copiedregion[attr] || parcellation[attr]
+    }
+    processRegion(copiedregion)
+  }
+
+  return map
+}
+
+export interface IMeshesToLoad {
+  labelIndicies: number[]
+  layer: {
+    name: string
+  }
+}
+
+export const SET_MESHES_TO_LOAD = new InjectionToken<Observable<IMeshesToLoad>>('SET_MESHES_TO_LOAD')
diff --git a/src/viewerModule/nehuba/index.ts b/src/viewerModule/nehuba/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..65cb900d6d7293034929898b2515f02082d04b37
--- /dev/null
+++ b/src/viewerModule/nehuba/index.ts
@@ -0,0 +1,4 @@
+export { NehubaGlueCmp } from "./nehubaViewerGlue/nehubaViewerGlue.component"
+export { NehubaViewerTouchDirective } from "./nehubaViewerInterface/nehubaViewerTouch.directive"
+export { NehubaModule } from "./module"
+export { NehubaViewerUnit } from "./nehubaViewer/nehubaViewer.component"
diff --git a/src/viewerModule/nehuba/layerCtrl.service/index.ts b/src/viewerModule/nehuba/layerCtrl.service/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..05cbb34fe0a5803c935749c2c45e6345bb467746
--- /dev/null
+++ b/src/viewerModule/nehuba/layerCtrl.service/index.ts
@@ -0,0 +1,11 @@
+export {
+  NehubaLayerControlService,
+} from './layerCtrl.service'
+
+export {
+  IColorMap,
+  SET_COLORMAP_OBS,
+  SET_LAYER_VISIBILITY,
+  getRgb,
+  INgLayerInterface,
+} from './layerCtrl.util'
diff --git a/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.spec.ts b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7e41087b16ceae99fe24b630a720d5622cc44c4d
--- /dev/null
+++ b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.spec.ts
@@ -0,0 +1,422 @@
+import { fakeAsync, TestBed, tick } from "@angular/core/testing"
+import { MockStore, provideMockStore } from "@ngrx/store/testing"
+import { viewerStateCustomLandmarkSelector, viewerStateSelectedParcellationSelector, viewerStateSelectedRegionsSelector, viewerStateSelectedTemplateSelector } from "src/services/state/viewerState/selectors"
+import { NehubaLayerControlService } from "./layerCtrl.service"
+import * as layerCtrlUtil from '../constants'
+import { hot } from "jasmine-marbles"
+import { IColorMap } from "./layerCtrl.util"
+import { debounceTime } from "rxjs/operators"
+import { ngViewerSelectorClearView, ngViewerSelectorLayers } from "src/services/state/ngViewerState.store.helper"
+
+describe('> layerctrl.service.ts', () => {
+  describe('> NehubaLayerControlService', () => {
+    let mockStore: MockStore
+    let getMultiNgIdsRegionsLabelIndexMapSpy: jasmine.Spy
+    let getMultiNgIdsRegionsLabelIndexMapReturnVal: Map<string, Map<number, layerCtrlUtil.IRegion>>
+    beforeEach(() => {
+      TestBed.configureTestingModule({
+        providers: [
+          NehubaLayerControlService,
+          provideMockStore()
+        ]
+      })
+
+      mockStore = TestBed.inject(MockStore)
+      getMultiNgIdsRegionsLabelIndexMapReturnVal = new Map()
+      getMultiNgIdsRegionsLabelIndexMapSpy = spyOnProperty(
+        layerCtrlUtil,
+        'getMultiNgIdsRegionsLabelIndexMap'
+      ).and.returnValue(() => getMultiNgIdsRegionsLabelIndexMapReturnVal)
+      mockStore.overrideSelector(viewerStateCustomLandmarkSelector, [])
+      mockStore.overrideSelector(viewerStateSelectedRegionsSelector, [])
+      mockStore.overrideSelector(viewerStateSelectedTemplateSelector, {})
+      mockStore.overrideSelector(viewerStateSelectedParcellationSelector, {})
+    })
+
+    it('> can be init', () => {
+      const service = TestBed.inject(NehubaLayerControlService)
+      expect(service).toBeTruthy()
+    })
+
+    describe('> setColorMap$', () => {
+      describe('> overwriteColorMap$ not firing', () => {
+        describe('> template/parc has no aux meshes', () => {
+
+          it('> calls getMultiNgIdsRegionsLabelIndexMapReturn', () => {
+            const service = TestBed.inject(NehubaLayerControlService)
+            service.setColorMap$.subscribe()
+            expect(getMultiNgIdsRegionsLabelIndexMapSpy).toHaveBeenCalled()
+          })
+
+          it('> emitted value is as expected', fakeAsync(() => {
+            const map = new Map<number, layerCtrlUtil.IRegion>()
+            getMultiNgIdsRegionsLabelIndexMapReturnVal.set(
+              'foo-bar',
+              map
+            )
+            map.set(1, {
+              ngId: 'foo-bar',
+              rgb: [100, 200, 255]
+            })
+            map.set(2, {
+              ngId: 'foo-bar',
+              rgb: [15, 15, 15]
+            })
+
+            const service = TestBed.inject(NehubaLayerControlService)
+            let v: any
+            service.setColorMap$.subscribe(val => {
+              v = val
+            })
+            tick(32)
+            const expectedVal = {
+              'foo-bar': {
+                1: { red: 100, green: 200, blue: 255 },
+                2: { red: 15, green: 15, blue: 15}
+              }
+            }
+            expect(v).toEqual(expectedVal)
+          }))
+
+        })
+
+        describe('> template/parc has aux meshes', () => {
+          let tmplAuxMeshes = [{
+            name: 'foo-bar',
+            ngId: 'bazz',
+            labelIndicies: [1,2,3],
+            rgb: [100, 100, 100]
+          }, {
+            name: 'hello-world',
+            ngId: 'hello-world',
+            labelIndicies: [4,5,6],
+            rgb: [200, 200, 200]
+          }]
+          let parcAuxMeshes = [{
+            name: 'hello-world',
+            ngId: 'hello-world',
+            labelIndicies: [10,11,12],
+            rgb: [255, 255, 255]
+          }]
+          beforeEach(() => {
+            mockStore.overrideSelector(viewerStateSelectedTemplateSelector, {
+              auxMeshes: tmplAuxMeshes
+            })
+            mockStore.overrideSelector(viewerStateSelectedParcellationSelector, {
+              auxMeshes: parcAuxMeshes
+            })
+          })
+
+          it('> should inherit values from tmpl and parc',  fakeAsync(() => {
+
+            const service = TestBed.inject(NehubaLayerControlService)
+            let val
+            service.setColorMap$.subscribe(v => {
+              val = v
+            })
+
+            tick(32)
+
+            expect(val).toEqual({
+              'bazz': {
+                1: { red: 100, green: 100, blue: 100 },
+                2: { red: 100, green: 100, blue: 100 },
+                3: { red: 100, green: 100, blue: 100 },
+              },
+              'hello-world': {
+                4: { red: 200, green: 200, blue: 200 },
+                5: { red: 200, green: 200, blue: 200 },
+                6: { red: 200, green: 200, blue: 200 },
+                10: { red: 255, green: 255, blue: 255 },
+                11: { red: 255, green: 255, blue: 255 },
+                12: { red: 255, green: 255, blue: 255 },
+              }
+            })
+          }))
+
+          it('> should overwrite any value if at all, from region', fakeAsync(() => {
+            const map = new Map<number, layerCtrlUtil.IRegion>()
+            map.set(10, {
+              ngId: 'hello-world',
+              rgb: [0, 0, 0]
+            })
+            map.set(15, {
+              ngId: 'hello-world',
+              rgb: [0, 0, 0]
+            })
+            getMultiNgIdsRegionsLabelIndexMapReturnVal.set('hello-world', map)
+
+            const service = TestBed.inject(NehubaLayerControlService)
+            let val
+            service.setColorMap$.subscribe(v => {
+              val = v
+            })
+
+            tick(32)
+            expect(val).toEqual({
+              'bazz': {
+                1: { red: 100, green: 100, blue: 100 },
+                2: { red: 100, green: 100, blue: 100 },
+                3: { red: 100, green: 100, blue: 100 },
+              },
+              'hello-world': {
+                4: { red: 200, green: 200, blue: 200 },
+                5: { red: 200, green: 200, blue: 200 },
+                6: { red: 200, green: 200, blue: 200 },
+                10: { red: 255, green: 255, blue: 255 },
+                11: { red: 255, green: 255, blue: 255 },
+                12: { red: 255, green: 255, blue: 255 },
+                15: { red: 0, green: 0, blue: 0 },
+              }
+            })
+          }))
+        })
+      })
+
+      const foobar1 = {
+        'foo-bar': {
+          1: { red: 100, green: 200, blue: 255 },
+          2: { red: 15, green: 15, blue: 15 },
+        }
+      }
+      const foobar2 = {
+        'foo-bar': {
+          2: { red: 255, green: 255, blue: 255 },
+        }
+      }
+    
+      describe('> overwriteColorMap$ firing', () => {
+        beforeEach(() => {
+          mockStore.overrideSelector(viewerStateSelectedTemplateSelector, {})
+          mockStore.overrideSelector(viewerStateSelectedParcellationSelector, {})
+          const map = new Map<number, layerCtrlUtil.IRegion>()
+          getMultiNgIdsRegionsLabelIndexMapReturnVal.set(
+            'foo-bar',
+            map
+          )
+          map.set(1, {
+            ngId: 'foo-bar',
+            rgb: [100, 200, 255]
+          })
+          map.set(2, {
+            ngId: 'foo-bar',
+            rgb: [15, 15, 15]
+          })
+        })
+
+        it('> should overwrite existing colormap', () => {
+          const service = TestBed.inject(NehubaLayerControlService)
+          service.overwriteColorMap$.next(foobar2)
+
+          expect(service.setColorMap$).toBeObservable(
+            hot('(b)', {
+              a: foobar1,
+              b: foobar2
+            })
+          )
+        })
+
+        it('> unsub/resub should not result in overwritecolormap last emitted value', fakeAsync(() => {
+          const service = TestBed.inject(NehubaLayerControlService)
+
+          let subscrbiedVal: IColorMap
+          const sub = service.setColorMap$.pipe(
+            debounceTime(16),
+          ).subscribe(val => {
+            subscrbiedVal = val
+          })
+
+          // see TODO this is a dirty fix
+          tick(32)
+          service.overwriteColorMap$.next(foobar2)
+          tick(32)
+          expect(subscrbiedVal).toEqual(foobar2)
+          tick(16)
+          sub.unsubscribe()
+          subscrbiedVal = null
+
+          // mock emit selectParc etc...
+          mockStore.overrideSelector(viewerStateSelectedParcellationSelector, {})
+          mockStore.setState({})
+          const sub2 = service.setColorMap$.pipe(
+            debounceTime(16),
+          ).subscribe(val => {
+            subscrbiedVal = val
+          })
+
+          tick(32)
+          expect(subscrbiedVal).toEqual(foobar1)
+          sub2.unsubscribe()
+
+        }))
+      })
+    })
+
+
+    describe('> visibleLayer$', () => {
+      beforeEach(() => {
+        mockStore.overrideSelector(viewerStateSelectedTemplateSelector, {
+          ngId: 'tmplNgId',
+          auxMeshes: [{
+            ngId: 'tmplAuxId1',
+            labelIndicies: [1,2,3]
+          },{
+            ngId: 'tmplAuxId2',
+            labelIndicies: [1,2,3]
+          }]
+        })
+
+        mockStore.overrideSelector(viewerStateSelectedParcellationSelector, {
+          auxMeshes: [{
+            ngId: 'parcAuxId1',
+            labelIndicies: [1,2,3],
+          },{
+            ngId: 'parcAuxId2',
+            labelIndicies: [1,2,3]
+          }]
+        })
+
+        getMultiNgIdsRegionsLabelIndexMapReturnVal.set(
+          'regionsNgId1', null
+        )
+
+        getMultiNgIdsRegionsLabelIndexMapReturnVal.set(
+          'regionsNgId2', null
+        )
+      })
+      it('> combines ngId of template, aux mesh and regions', () => {
+        const service = TestBed.inject(NehubaLayerControlService)
+        expect(service.visibleLayer$).toBeObservable(hot('a', {
+          a: [
+            'tmplNgId',
+            'tmplAuxId1',
+            'tmplAuxId2',
+            'parcAuxId1',
+            'parcAuxId2',
+            'regionsNgId1',
+            'regionsNgId2',
+          ]
+        }))
+      })
+    })
+
+    describe('> segmentVis$', () => {
+      const region1= {
+        ngId: 'ngid',
+        labelIndex: 1
+      }
+      const region2= {
+        ngId: 'ngid',
+        labelIndex: 2
+      }
+      beforeEach(() => {
+        mockStore.overrideSelector(viewerStateSelectedRegionsSelector, [])
+        mockStore.overrideSelector(ngViewerSelectorLayers, [])
+        mockStore.overrideSelector(ngViewerSelectorClearView, false)
+        mockStore.overrideSelector(viewerStateSelectedParcellationSelector, {})
+      })
+
+      it('> by default, should return []', () => {
+        const service = TestBed.inject(NehubaLayerControlService)
+        expect(service.segmentVis$).toBeObservable(
+          hot('a', {
+            a: []
+          })
+        )
+      })
+
+      describe('> if sel regions exist', () => {
+        beforeEach(() => {
+          mockStore.overrideSelector(viewerStateSelectedRegionsSelector, [
+            region1, region2
+          ])
+        })
+
+        it('> default, should return encoded strings', () => {
+          mockStore.overrideSelector(viewerStateSelectedRegionsSelector, [
+            region1, region2
+          ])
+          const service = TestBed.inject(NehubaLayerControlService)
+          expect(service.segmentVis$).toBeObservable(
+            hot('a', {
+              a: [`ngid#1`, `ngid#2`]
+            })
+          )
+        })
+
+        it('> if clearflag is true, then return []', () => {
+
+          mockStore.overrideSelector(ngViewerSelectorClearView, true)
+          const service = TestBed.inject(NehubaLayerControlService)
+          expect(service.segmentVis$).toBeObservable(
+            hot('a', {
+              a: []
+            })
+          )
+        })        
+      })
+
+      describe('> if non mixable layer exist', () => {
+        beforeEach(() => {
+          mockStore.overrideSelector(ngViewerSelectorLayers, [{
+            mixability: 'nonmixable'
+          }])
+        })
+
+        it('> default, should return null', () => {
+          const service = TestBed.inject(NehubaLayerControlService)
+          expect(service.segmentVis$).toBeObservable(
+            hot('a', {
+              a: null
+            })
+          )
+        })
+
+        it('> if regions selected, should still return null', () => {
+
+          mockStore.overrideSelector(viewerStateSelectedRegionsSelector, [
+            region1, region2
+          ])
+          const service = TestBed.inject(NehubaLayerControlService)
+          expect(service.segmentVis$).toBeObservable(
+            hot('a', {
+              a: null
+            })
+          )
+        })
+
+        describe('> if clear flag is set', () => {
+          beforeEach(() => {
+            mockStore.overrideSelector(ngViewerSelectorClearView, true)
+          })
+
+          it('> default, should return []', () => {
+            const service = TestBed.inject(NehubaLayerControlService)
+            expect(service.segmentVis$).toBeObservable(
+              hot('a', {
+                a: []
+              })
+            )
+          })
+
+          it('> if reg selected, should return []', () => {
+
+            mockStore.overrideSelector(viewerStateSelectedRegionsSelector, [
+              region1, region2
+            ])
+            const service = TestBed.inject(NehubaLayerControlService)
+            expect(service.segmentVis$).toBeObservable(
+              hot('a', {
+                a: []
+              })
+            )
+          })
+        })
+      })
+    })
+
+    describe('> ngLayersController$', () => {
+      
+    })
+  })
+})
diff --git a/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.ts b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..56e87c06579b66207560caf92611ca2ab83b6c39
--- /dev/null
+++ b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.ts
@@ -0,0 +1,423 @@
+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 { viewerStateCustomLandmarkSelector, viewerStateSelectedParcellationSelector, viewerStateSelectedRegionsSelector, viewerStateSelectedTemplateSelector } from "src/services/state/viewerState/selectors";
+import { getRgb, IColorMap, INgLayerCtrl, INgLayerInterface, TNgLayerCtrl } from "./layerCtrl.util";
+import { getMultiNgIdsRegionsLabelIndexMap } from "../constants";
+import { IAuxMesh } from '../store'
+import { REGION_OF_INTEREST } from "src/util/interfaces";
+import { TRegionDetail } from "src/util/siibraApiConstants/types";
+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'
+
+export const BACKUP_COLOR = {
+  red: 255,
+  green: 255,
+  blue: 255
+}
+
+export function getAuxMeshesAndReturnIColor(auxMeshes: IAuxMesh[]): IColorMap{
+  const returnVal: IColorMap = {}
+  for (const auxMesh of auxMeshes as IAuxMesh[]) {
+    const { ngId, labelIndicies, rgb = [255, 255, 255] } = auxMesh
+    const auxMeshColorMap = returnVal[ngId] || {}
+    for (const lblIdx of labelIndicies) {
+      auxMeshColorMap[lblIdx as number] = {
+        red: rgb[0] as number,
+        green: rgb[1] as number,
+        blue: rgb[2] as number,
+      }
+    }
+    returnVal[ngId] = auxMeshColorMap
+  }
+  return returnVal
+}
+
+@Injectable()
+export class NehubaLayerControlService implements OnDestroy{
+
+  static PMAP_LAYER_NAME = 'regional-pmap'
+
+  private selectedRegion$ = this.store$.pipe(
+    select(viewerStateSelectedRegionsSelector),
+    shareReplay(1),
+  )
+
+  private selectedParcellation$ = this.store$.pipe(
+    select(viewerStateSelectedParcellationSelector)
+  )
+
+  private selectedTemplateSelector$ = this.store$.pipe(
+    select(viewerStateSelectedTemplateSelector)
+  )
+
+  private selParcNgIdMap$ = this.selectedParcellation$.pipe(
+    map(parc => getMultiNgIdsRegionsLabelIndexMap(parc)),
+    shareReplay(1),
+  )
+  
+  private activeColorMap$: Observable<IColorMap> = combineLatest([
+    this.selParcNgIdMap$.pipe(
+      map(map => {
+        const returnVal: IColorMap = {}
+        for (const [ key, val ] of map.entries()) {
+          returnVal[key] = {}
+          for (const [ lblIdx, region ] of val.entries()) {
+            const rgb = getRgb(lblIdx, region)
+            returnVal[key][lblIdx] = rgb
+          }
+        }
+        return returnVal
+      })
+    ),
+    this.selectedRegion$,
+    this.selectedTemplateSelector$.pipe(
+      map(template => {
+        const { auxMeshes = [] } = template || {}
+        return getAuxMeshesAndReturnIColor(auxMeshes)
+      })
+    ),
+    this.selectedParcellation$.pipe(
+      map(parc => {
+        const { auxMeshes = [] } = parc || {}
+        return getAuxMeshesAndReturnIColor(auxMeshes)
+      })
+    ),
+  ]).pipe(
+    map(([ regions, selReg, ...auxMeshesArr ]) => {
+      
+      const returnVal: IColorMap = {}
+      if (selReg.length === 0) {
+        for (const key in regions) {
+          returnVal[key] = regions[key]
+        }
+      } else {
+        /**
+         * if selected regions are non empty
+         * set the selected regions to show color,
+         * but the rest to show white 
+         */
+        for (const key in regions) {
+          const colorMap = {}
+          returnVal[key] = colorMap
+          for (const lblIdx in regions[key]) {
+            if (selReg.some(r => r.ngId === key && r.labelIndex === Number(lblIdx))) {
+              colorMap[lblIdx] = regions[key][lblIdx]
+            } else {
+              colorMap[lblIdx] = BACKUP_COLOR
+            }
+          }
+        }
+      }
+
+      for (const auxMeshes of auxMeshesArr) {
+        for (const key in auxMeshes) {
+          const existingObj = returnVal[key] || {}
+          returnVal[key] = {
+            ...existingObj,
+            ...auxMeshes[key],
+          }
+        }
+      }
+      this.activeColorMap = returnVal
+      return returnVal
+    })
+  )
+
+  private auxMeshes$: Observable<IAuxMesh[]> = combineLatest([
+    this.selectedTemplateSelector$,
+    this.selectedParcellation$,
+  ]).pipe(
+    map(([ tmpl, parc ]) => {
+      const { auxMeshes: tmplAuxMeshes = [] as IAuxMesh[] } = tmpl || {}
+      const { auxMeshes: parclAuxMeshes = [] as IAuxMesh[] } = parc || {}
+      return [...tmplAuxMeshes, ...parclAuxMeshes]
+    })
+  )
+
+  private sub: Subscription[] = []
+
+  ngOnDestroy(){
+    while (this.sub.length > 0) this.sub.pop().unsubscribe()
+  }
+
+  constructor(
+    private store$: Store<any>,
+    @Optional() @Inject(REGION_OF_INTEREST) roi$: Observable<TRegionDetail>
+  ){
+    if (roi$) {
+
+      this.sub.push(
+        roi$.pipe(
+          switchMap(roi => {
+            if (!roi || !roi.hasRegionalMap) {
+              // clear pmap
+              return of(null)
+            }
+            
+            const { links } = roi
+            const { regional_map: regionalMapUrl, regional_map_info: regionalMapInfoUrl } = links
+            return from(fetch(regionalMapInfoUrl).then(res => res.json())).pipe(
+              map(regionalMapInfo => {
+                return {
+                  roi,
+                  regionalMapUrl,
+                  regionalMapInfo
+                }
+              })
+            )
+          })
+        ).subscribe(processedRoi => {
+          if (!processedRoi) {
+            this.store$.dispatch(
+              ngViewerActionRemoveNgLayer({
+                layer: {
+                  name: NehubaLayerControlService.PMAP_LAYER_NAME
+                }
+              })
+            )
+            return
+          }
+          const { 
+            roi,
+            regionalMapUrl,
+            regionalMapInfo
+          } = processedRoi
+          const { min, max, colormap = EnumColorMapName.VIRIDIS } = regionalMapInfo || {} as any
+
+          const shaderObj = {
+            ...PMAP_DEFAULT_CONFIG,
+            ...{ colormap },
+            ...( typeof min !== 'undefined' ? { lowThreshold: min } : {} ),
+            ...( max ? { highThreshold: max } : { highThreshold: 1 } )
+          }
+
+          const layer = {
+            name: NehubaLayerControlService.PMAP_LAYER_NAME,
+            source : `nifti://${regionalMapUrl}`,
+            mixability : 'nonmixable',
+            shader : getShader(shaderObj),
+          }
+
+          this.store$.dispatch(
+            ngViewerActionAddNgLayer({ layer })
+          )
+
+          // this.layersService.highThresholdMap.set(layerName, highThreshold)
+          // this.layersService.lowThresholdMap.set(layerName, lowThreshold)
+          // this.layersService.colorMapMap.set(layerName, cmap)
+          // this.layersService.removeBgMap.set(layerName, removeBg)
+        })
+      )
+    }
+
+    this.sub.push(
+      this.ngLayers$.subscribe(({ ngLayers }) => {
+        this.ngLayersRegister.layers = ngLayers
+      })
+    )
+
+    this.sub.push(
+      this.store$.pipe(
+        select(ngViewerSelectorClearView),
+        distinctUntilChanged()
+      ).subscribe(flag => {
+        const pmapLayer = this.ngLayersRegister.layers.find(l => l.name === NehubaLayerControlService.PMAP_LAYER_NAME)
+        if (!pmapLayer) return
+        const payload = {
+          type: 'update',
+          payload: {
+            [NehubaLayerControlService.PMAP_LAYER_NAME]: {
+              visible: !flag
+            }
+          }
+        } as TNgLayerCtrl<'update'>
+        this.manualNgLayersControl$.next(payload)
+      })
+    )
+
+    /**
+     * on custom landmarks loaded, set mesh transparency
+     */
+    this.sub.push(
+      this.store$.pipe(
+        select(viewerStateCustomLandmarkSelector),
+        withLatestFrom(this.auxMeshes$)
+      ).subscribe(([landmarks, auxMeshes]) => {
+        
+        const payload: {
+          [key: string]: number
+        } = {}
+        const alpha = landmarks.length > 0
+          ? 0.2
+          : 1.0
+        for (const auxMesh of auxMeshes) {
+          payload[auxMesh.ngId] = alpha
+        }
+        
+        this.manualNgLayersControl$.next({
+          type: 'setLayerTransparency',
+          payload
+        })
+      })
+    )
+  }
+
+  public activeColorMap: IColorMap
+
+  public overwriteColorMap$ = new BehaviorSubject<IColorMap>(null)
+
+  public setColorMap$: Observable<IColorMap> = merge(
+    this.activeColorMap$.pipe(
+      // TODO this is a dirty fix
+      // it seems, sometimes, overwritecolormap and activecolormap can emit at the same time
+      // (e.g. when reg selection changes)
+      // this ensures that the activecolormap emits later, and thus take effect over overwrite colormap
+      debounceTime(16),
+    ),
+    this.overwriteColorMap$.pipe(
+      filter(v => !!v),
+    )
+  ).pipe(
+    shareReplay(1)
+  )
+
+  public visibleLayer$: Observable<string[]> = combineLatest([
+    this.selectedTemplateSelector$,
+    this.auxMeshes$,
+    this.selParcNgIdMap$
+  ]).pipe(
+    map(([ tmpl, auxMeshes, parcNgIdMap ]) => {
+      const ngIdSet = new Set<string>()
+      const { ngId } = tmpl
+      ngIdSet.add(ngId)
+      for (const auxMesh of auxMeshes) {
+        const { ngId } = auxMesh
+        ngIdSet.add(ngId as string)
+      }
+      for (const ngId of parcNgIdMap.keys()) {
+        ngIdSet.add(ngId)
+      }
+      return Array.from(ngIdSet)
+    })
+  )
+
+  /**
+   * define when shown segments should be updated
+   */
+  public segmentVis$: Observable<string[]> = combineLatest([
+    /**
+     * selectedRegions
+     */
+    this.selectedRegion$,
+    /**
+     * if layer contains non mixable layer
+     */
+    this.store$.pipe(
+      select(ngViewerSelectorLayers),
+      map(layers => layers.findIndex(l => l.mixability === 'nonmixable') >= 0),
+    ),
+    /**
+     * clearviewqueue, indicating something is controlling colour map
+     * show all seg
+     */
+    this.store$.pipe(
+      select(ngViewerSelectorClearView),
+      distinctUntilChanged()
+    )
+  ]).pipe(
+    withLatestFrom(this.selectedParcellation$),
+    map(([[ regions, nonmixableLayerExists, clearViewFlag ], selParc]) => {
+      if (nonmixableLayerExists && !clearViewFlag) {
+        return null
+      }
+      const { ngId: defaultNgId } = selParc || {}
+  
+      /* selectedregionindexset needs to be updated regardless of forceshowsegment */
+      const selectedRegionIndexSet = new Set<string>(regions.map(({ngId = defaultNgId, labelIndex}) => serialiseParcellationRegion({ ngId, labelIndex })))
+      if (selectedRegionIndexSet.size > 0 && !clearViewFlag) {
+        return [...selectedRegionIndexSet]
+      } else {
+        return []
+      }
+    })
+  )
+
+  /**
+   * ngLayers controller
+   */
+
+  private ngLayersRegister: {layers: INgLayerInterface[]} = {
+    layers: []
+  }
+  public removeNgLayers(layerNames: string[]) {
+    this.ngLayersRegister.layers
+      .filter(layer => layerNames?.findIndex(l => l === layer.name) >= 0)
+      .map(l => l.name)
+      .forEach(layerName => {
+        this.store$.dispatch(ngViewerActionRemoveNgLayer({
+          layer: {
+            name: layerName
+          }
+        }))
+      })
+  }
+  public addNgLayer(layers: INgLayerInterface[]){
+    this.store$.dispatch(ngViewerActionAddNgLayer({
+      layer: layers
+    }))
+  }
+  private ngLayers$ = this.store$.pipe(
+    select(ngViewerSelectorLayers),
+    map((ngLayers: INgLayerInterface[]) => {
+      const newLayers = ngLayers.filter(l => {
+        const registeredLayerNames = this.ngLayersRegister.layers.map(l => l.name)
+        return !registeredLayerNames.includes(l.name)
+      })
+      const removeLayers = this.ngLayersRegister.layers.filter(l => {
+        const stateLayerNames = ngLayers.map(l => l.name)
+        return !stateLayerNames.includes(l.name)
+      })
+      return { newLayers, removeLayers, ngLayers }
+    }),
+    shareReplay(1)
+  )
+  private manualNgLayersControl$ = new Subject<TNgLayerCtrl<keyof INgLayerCtrl>>()
+  ngLayersController$: Observable<TNgLayerCtrl<keyof INgLayerCtrl>> = merge(
+    this.ngLayers$.pipe(
+      map(({ newLayers }) => newLayers),
+      filter(layers => layers.length > 0),
+      map(newLayers => {
+
+        const newLayersObj: any = {}
+        newLayers.forEach(({ name, source, ...rest }) => newLayersObj[name] = {
+          ...rest,
+          source,
+          // source: getProxyUrl(source),
+          // ...getProxyOther({source})
+        })
+  
+        return {
+          type: 'add',
+          payload: newLayersObj
+        } as TNgLayerCtrl<'add'>
+      })
+    ),
+    this.ngLayers$.pipe(
+      map(({ removeLayers }) => removeLayers),
+      filter(layers => layers.length > 0),
+      map(removeLayers => {
+        const removeLayerNames = removeLayers.map(v => v.name)
+        return {
+          type: 'remove',
+          payload: { names: removeLayerNames }
+        } as TNgLayerCtrl<'remove'>
+      })
+    ),
+    this.manualNgLayersControl$,
+  ).pipe(
+  )
+}
diff --git a/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.util.spec.ts b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.util.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..62aa8819cf104fcd966663ed3b39d3b860ea6f63
--- /dev/null
+++ b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.util.spec.ts
@@ -0,0 +1,70 @@
+import * as util from 'common/util'
+import { getRgb } from './layerCtrl.util'
+
+describe('> layerCtrl.util.ts', () => {
+  describe('> #getRgb', () => {
+    let strToRgbSpy: jasmine.Spy
+    let strToRgbValue: [number, number, number]
+    beforeEach(() => {
+      strToRgbSpy = spyOn(util, 'strToRgb')
+      strToRgbValue = [1,2,3]
+      strToRgbSpy.and.returnValue(strToRgbValue)
+    })
+
+    describe('> region has rgb defined', () => {
+      const labelIndex = 1020
+      const region = {
+        ngId: 'foo-bar',
+        rgb: [100, 200, 255] as [number, number, number]
+      }
+      it('> should return region rgb', () => {
+        expect(
+          getRgb(labelIndex, region)
+        ).toEqual({
+          red: 100,
+          green: 200,
+          blue: 255
+        })
+      })
+    })
+
+    describe('> if region does not have rgb defined', () => {
+      describe('> if labelIndex > 65500', () => {
+        const region = {
+          ngId: 'foo-bar',
+        }
+        const labelIndex = 65535
+        it('> should return white', () => {
+          expect(
+            getRgb(labelIndex, region)
+          ).toEqual({
+            red: 255,
+            green: 255,
+            blue: 255
+          })
+        })
+      })
+
+      describe('> otherwise', () => {
+        const region = {
+          ngId: 'foo-bar',
+        }
+        const labelIndex = 12
+        it('> should call strToRgb', () => {
+          getRgb(labelIndex, region)
+          expect(strToRgbSpy).toHaveBeenCalledWith(`${region.ngId}${labelIndex}`)
+        })
+
+        it('> returns what strToRgb returns', () => {
+          expect(
+            getRgb(labelIndex, region)
+          ).toEqual({
+            red: 1,
+            green: 2,
+            blue: 3
+          })
+        })
+      })
+    })
+  })
+})
\ No newline at end of file
diff --git a/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.util.ts b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.util.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f6fc08184e0d31d4ed9229ca6840b9981ac8c4f2
--- /dev/null
+++ b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.util.ts
@@ -0,0 +1,74 @@
+import { InjectionToken } from '@angular/core'
+import { strToRgb } from 'common/util'
+import { Observable } from 'rxjs'
+
+export interface IColorMap {
+  [key: string]: {
+    [key: number]: {
+      red: number
+      green: number
+      blue: number
+    }
+  }
+}
+
+export function getRgb(labelIndex: number, region: { rgb?: [number, number, number], ngId: string }): {red: number, green: number, blue: number} {
+  const { rgb, ngId } = region
+  if (typeof rgb === 'undefined' || rgb === null) {
+    if (labelIndex > 65500) {
+      return {
+        red: 255,
+        green: 255,
+        blue: 255
+      }
+    }
+    const arr = strToRgb(`${ngId}${labelIndex}`)
+    return {
+      red : arr[0],
+      green: arr[1],
+      blue : arr[2],
+    }
+  }
+  return {
+    red : rgb[0],
+    green: rgb[1],
+    blue : rgb[2],
+  }
+}
+
+
+export interface INgLayerCtrl {
+  remove: {
+    names: string[]
+  }
+  add: {
+    [key: string]: INgLayerInterface
+  }
+  update: {
+    [key: string]: INgLayerInterface
+  }
+  setLayerTransparency: {
+    [key: string]: number
+  }
+}
+
+export type TNgLayerCtrl<T extends keyof INgLayerCtrl> = {
+  type: T
+  payload: INgLayerCtrl[T]
+}
+
+export const SET_COLORMAP_OBS = new InjectionToken<Observable<IColorMap>>('SET_COLORMAP_OBS')
+export const SET_LAYER_VISIBILITY = new InjectionToken<Observable<string[]>>('SET_LAYER_VISIBILITY')
+export const SET_SEGMENT_VISIBILITY = new InjectionToken<Observable<string[]>>('SET_SEGMENT_VISIBILITY')
+export const NG_LAYER_CONTROL = new InjectionToken<TNgLayerCtrl<keyof INgLayerCtrl>>('NG_LAYER_CONTROL')
+
+export interface INgLayerInterface {
+  name: string // displayName
+  source: string
+  mixability: string // base | mixable | nonmixable
+  annotation?: string //
+  id?: string // unique identifier
+  visible?: boolean
+  shader?: string
+  transform?: any
+}
diff --git a/src/ui/nehubaContainer/maximisePanelButton/maximisePanelButton.component.ts b/src/viewerModule/nehuba/maximisePanelButton/maximisePanelButton.component.ts
similarity index 97%
rename from src/ui/nehubaContainer/maximisePanelButton/maximisePanelButton.component.ts
rename to src/viewerModule/nehuba/maximisePanelButton/maximisePanelButton.component.ts
index b3e5745f92e2ea7e9dbb2d95cc2298375b3a49b3..7aab44ac4156b05454a3d7bce915068cdf24a07e 100644
--- a/src/ui/nehubaContainer/maximisePanelButton/maximisePanelButton.component.ts
+++ b/src/viewerModule/nehuba/maximisePanelButton/maximisePanelButton.component.ts
@@ -19,7 +19,7 @@ const {
   ],
 })
 
-export class MaximmisePanelButton {
+export class MaximisePanelButton {
 
   public ARIA_LABEL_MAXIMISE_VIEW = MAXIMISE_VIEW
   public ARIA_LABEL_UNMAXIMISE_VIEW = UNMAXIMISE_VIEW
diff --git a/src/viewerModule/nehuba/maximisePanelButton/maximisePanelButton.style.css b/src/viewerModule/nehuba/maximisePanelButton/maximisePanelButton.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/ui/nehubaContainer/maximisePanelButton/maximisePanelButton.template.html b/src/viewerModule/nehuba/maximisePanelButton/maximisePanelButton.template.html
similarity index 100%
rename from src/ui/nehubaContainer/maximisePanelButton/maximisePanelButton.template.html
rename to src/viewerModule/nehuba/maximisePanelButton/maximisePanelButton.template.html
diff --git a/src/viewerModule/nehuba/mesh.service/index.ts b/src/viewerModule/nehuba/mesh.service/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..eba47eb2549b4fa67a698e4a002e9c570fe10a8a
--- /dev/null
+++ b/src/viewerModule/nehuba/mesh.service/index.ts
@@ -0,0 +1,3 @@
+export {
+  NehubaMeshService
+} from './mesh.service'
diff --git a/src/viewerModule/nehuba/mesh.service/mesh.service.spec.ts b/src/viewerModule/nehuba/mesh.service/mesh.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..967762e46acb61182a31dfc625772c14da8a2dca
--- /dev/null
+++ b/src/viewerModule/nehuba/mesh.service/mesh.service.spec.ts
@@ -0,0 +1,174 @@
+import { TestBed } from "@angular/core/testing"
+import { MockStore, provideMockStore } from "@ngrx/store/testing"
+import { hot } from "jasmine-marbles"
+import { viewerStateSelectedParcellationSelector, viewerStateSelectedRegionsSelector, viewerStateSelectedTemplateSelector } from "src/services/state/viewerState/selectors"
+import { selectorAuxMeshes } from "../store"
+import { getLayerNameIndiciesFromParcRs, collateLayerNameIndicies, findFirstChildrenWithLabelIndex, NehubaMeshService } from "./mesh.service"
+
+
+const fits1 = {
+  ngId: 'foobar',
+  labelIndex: 123,
+  children: []
+}
+
+const fits1_1 = {
+  ngId: 'foobar',
+  labelIndex: 5,
+  children: []
+}
+
+const fits2 = {
+  ngId: 'helloworld',
+  labelIndex: 567,
+  children: []
+}
+
+const fits2_1 = {
+  ngId: 'helloworld',
+  labelIndex: 11,
+  children: []
+}
+
+const nofit1 = {
+  ngId: 'bazz',
+  children: []
+}
+
+const nofit2 = {
+  ngId: 'but',
+  children: []
+}
+
+describe('> mesh.server.ts', () => {
+  describe('> findFirstChildrenWithLabelIndex', () => {
+    it('> if root fits, return root', () => {
+      const result = findFirstChildrenWithLabelIndex({
+        ...fits1,
+        children: [fits2]
+      })
+
+      expect(result).toEqual([{
+        ...fits1,
+        children: [fits2]
+      }])
+    })
+
+    it('> if root doesnt fit, will try to find the next node, until one fits', () => {
+      const result = findFirstChildrenWithLabelIndex({
+        ...nofit1,
+        children: [fits1, fits2]
+      })
+      expect(result).toEqual([fits1, fits2])
+    })
+
+    it('> if notthings fits, will return empty array', () => {
+      const result = findFirstChildrenWithLabelIndex({
+        ...nofit1,
+        children: [nofit1, nofit2]
+      })
+      expect(result).toEqual([])
+    })
+  })
+
+  describe('> collateLayerNameIndicies', () => {
+    it('> collates same ngIds', () => {
+      const result = collateLayerNameIndicies([
+        fits1_1, fits1, fits2, fits2_1
+      ])
+      expect(result).toEqual({
+        [fits1.ngId]: [fits1_1.labelIndex, fits1.labelIndex],
+        [fits2.ngId]: [fits2.labelIndex, fits2_1.labelIndex]
+      })
+    })
+  })
+
+  describe('> getLayerNameIndiciesFromParcRs', () => {
+    const root = {
+      ...fits1,
+      children: [
+        {
+          ...nofit1,
+          children: [
+            {
+              ...fits1_1,
+              children: [
+                fits2, fits2_1
+              ]
+            }
+          ]
+        }
+      ]
+    }
+    const parc = {
+      regions: [ root ]
+    }
+    it('> if selectedRegion.length === 0, selects top most regions with labelIndex', () => {
+      const result = getLayerNameIndiciesFromParcRs(parc, [])
+      expect(result).toEqual({
+        [root.ngId]: [root.labelIndex]
+      })
+    })
+
+    it('> if selReg.length !== 0, select region ngId & labelIndex', () => {
+      const result = getLayerNameIndiciesFromParcRs(parc, [ fits1_1 ])
+      expect(result).toEqual({
+        [fits1_1.ngId]: [fits1_1.labelIndex]
+      })
+    })
+  })
+
+  describe('> NehubaMeshService', () => {
+    beforeEach(() => {
+      TestBed.configureTestingModule({
+        providers: [
+          provideMockStore(),
+          NehubaMeshService,
+        ]
+      })
+      const mockStore = TestBed.inject(MockStore)
+      mockStore.overrideSelector(viewerStateSelectedParcellationSelector, {})
+      mockStore.overrideSelector(viewerStateSelectedTemplateSelector, {})
+    })
+
+    it('> can be init', () => {
+      const service = TestBed.inject(NehubaMeshService)
+      expect(service).toBeTruthy()
+    })
+
+    it('> mixes in auxillaryMeshIndices', () => {
+      const mockStore = TestBed.inject(MockStore)
+      mockStore.overrideSelector(viewerStateSelectedRegionsSelector, [ fits1 ])
+
+      mockStore.overrideSelector(selectorAuxMeshes, [{
+        ngId: fits2.ngId,
+        labelIndicies: [11, 22],
+        "@id": '',
+        name: '',
+        rgb: [100, 100, 100],
+        visible: true,
+        displayName: ''
+      }])
+
+      const service = TestBed.inject(NehubaMeshService)
+      expect(
+        service.loadMeshes$
+      ).toBeObservable(
+        hot('(ab)', {
+          a: {
+            layer: {
+              name: fits1.ngId
+            },
+            labelIndicies: [ fits1.labelIndex ]
+          },
+          b: {
+            layer: {
+              name: fits2.ngId,
+            },
+            labelIndicies: [11, 22]
+          }
+        })
+      )
+    })
+  })
+})
diff --git a/src/viewerModule/nehuba/mesh.service/mesh.service.ts b/src/viewerModule/nehuba/mesh.service/mesh.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..dd17226af19ad4c3ec2dfc8f10dbc79a96e53635
--- /dev/null
+++ b/src/viewerModule/nehuba/mesh.service/mesh.service.ts
@@ -0,0 +1,159 @@
+import { Injectable, OnDestroy } from "@angular/core";
+import { select, Store } from "@ngrx/store";
+import { combineLatest, Observable, of } from "rxjs";
+import { switchMap } from "rxjs/operators";
+import { viewerStateSelectedParcellationSelector, viewerStateSelectedRegionsSelector, viewerStateSelectedTemplateSelector } from "src/services/state/viewerState/selectors";
+import { IMeshesToLoad } from '../constants'
+import { flattenReducer } from 'common/util'
+import { IAuxMesh, selectorAuxMeshes, actionSetAuxMeshes } from "../store";
+
+interface IRegion {
+  ngId?: string
+  labelIndex?: number
+  children: IRegion[]
+}
+
+interface IParc {
+  ngId?: string
+  regions: IRegion[]
+}
+
+type TCollatedLayerNameIdx = {
+  [key: string]: number[]
+}
+
+export function findFirstChildrenWithLabelIndex(region: IRegion): IRegion[]{
+  if (region.ngId && region.labelIndex) {
+    return [ region ]
+  }
+  return region.children
+    .map(findFirstChildrenWithLabelIndex)
+    .reduce(flattenReducer, [])
+}
+
+export function collateLayerNameIndicies(regions: IRegion[]){
+  const returnObj: TCollatedLayerNameIdx = {}
+  for (const r of regions) {
+    if (returnObj[r.ngId]) {
+      returnObj[r.ngId].push(r.labelIndex)
+    } else {
+      returnObj[r.ngId] = [r.labelIndex]
+    }
+  }
+  return returnObj
+}
+
+export function getLayerNameIndiciesFromParcRs(parc: IParc, rs: IRegion[]): TCollatedLayerNameIdx {
+
+  const arrOfRegions = (rs.length === 0 ? parc.regions : rs)
+    .map(findFirstChildrenWithLabelIndex)
+    .reduce(flattenReducer, []) as IRegion[]
+
+  return collateLayerNameIndicies(arrOfRegions)
+}
+
+/**
+ * control mesh loading etc
+ */
+
+@Injectable()
+export class NehubaMeshService implements OnDestroy {
+
+  private onDestroyCb: (() => void)[] = []
+
+  constructor(
+    private store$: Store<any>
+  ){
+    const auxMeshSub = combineLatest([
+      this.selectedTemplate$,
+      this.selectedParc$
+    ]).subscribe(([ tmpl, parc ]) => {
+      const { auxMeshes: tmplAuxMeshes = [] as IAuxMesh[] } = tmpl || {}
+      const { auxMeshes: parcAuxMeshes = [] as IAuxMesh[]} = parc || {}
+      this.store$.dispatch(
+        actionSetAuxMeshes({
+          payload: [...tmplAuxMeshes, ...parcAuxMeshes]
+        })
+      )
+    })
+    this.onDestroyCb.push(() => auxMeshSub.unsubscribe())
+  }
+
+  ngOnDestroy(){
+    while(this.onDestroyCb.length > 0) this.onDestroyCb.pop()()
+  }
+
+  private selectedTemplate$ = this.store$.pipe(
+    select(viewerStateSelectedTemplateSelector)
+  )
+
+  private selectedRegions$ = this.store$.pipe(
+    select(viewerStateSelectedRegionsSelector)
+  )
+
+  private selectedParc$ = this.store$.pipe(
+    select(viewerStateSelectedParcellationSelector)
+  )
+
+  private auxMeshes$ = this.store$.pipe(
+    select(selectorAuxMeshes),
+  )
+
+  public loadMeshes$: Observable<IMeshesToLoad> = combineLatest([
+    this.auxMeshes$,
+    this.selectedTemplate$,
+    this.selectedParc$,
+    this.selectedRegions$,
+  ]).pipe(
+    switchMap(([auxMeshes, template, parc, selRegions]) => {
+      
+      /**
+       * if colin 27 and julich brain 2.9.0, select all regions
+       */
+      let overrideSelRegion = null
+      if (
+        template['@id'] === 'minds/core/referencespace/v1.0.0/7f39f7be-445b-47c0-9791-e971c0b6d992' &&
+        parc['@id'] === 'minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-290'
+      ) {
+        overrideSelRegion = []
+      }
+
+      const obj = getLayerNameIndiciesFromParcRs(parc, overrideSelRegion || selRegions)
+      const { auxillaryMeshIndices = [] } = parc
+      const arr: IMeshesToLoad[] = []
+      for (const key in obj) {
+        const labelIndicies = Array.from(new Set([...obj[key], ...auxillaryMeshIndices]))
+        arr.push({
+          layer: {
+            name: key
+          },
+          labelIndicies
+        })
+      }
+      
+      const auxLayers: {
+        [key: string]: number[]
+      } = {}
+
+      for (const auxMesh of auxMeshes) {
+        const { name, ngId, labelIndicies } = auxMesh
+        if (!auxLayers[ngId]) {
+          auxLayers[ngId] = []
+        }
+        if (auxMesh.visible) {
+          auxLayers[ngId].push(...labelIndicies)
+        }
+      }
+      for (const key in auxLayers) {
+        arr.push({
+          layer: {
+            name: key
+          },
+          labelIndicies: auxLayers[key]
+        })
+      }
+
+      return of(...arr)
+    }),
+  )
+}
diff --git a/src/viewerModule/nehuba/module.ts b/src/viewerModule/nehuba/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ea0c01d87e5432bfaad22a7b91ae7edb373fac55
--- /dev/null
+++ b/src/viewerModule/nehuba/module.ts
@@ -0,0 +1,85 @@
+import { NgModule } from "@angular/core";
+import { NehubaViewerContainerDirective } from './nehubaViewerInterface/nehubaViewerInterface.directive'
+import { IMPORT_NEHUBA_INJECT_TOKEN, NehubaViewerUnit } from "./nehubaViewer/nehubaViewer.component";
+import { CommonModule } from "@angular/common";
+import { APPEND_SCRIPT_TOKEN } from "src/util/constants";
+import { importNehubaFactory, NEHUBA_INSTANCE_INJTKN } from "./util";
+import { NehubaViewerTouchDirective } from "./nehubaViewerInterface/nehubaViewerTouch.directive";
+import { StoreModule } from "@ngrx/store";
+import { NEHUBA_VIEWER_FEATURE_KEY } from "./constants";
+import { reducer } from "./store";
+import { NehubaGlueCmp } from "./nehubaViewerGlue/nehubaViewerGlue.component";
+import { UtilModule } from "src/util";
+import { LayoutModule } from "src/layouts/layout.module";
+import { TouchSideClass } from "./touchSideClass.directive";
+import { ComponentsModule } from "src/components";
+import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module";
+import { MaximisePanelButton } from "./maximisePanelButton/maximisePanelButton.component";
+import { Landmark2DModule } from "src/ui/nehubaContainer/2dLandmarks/module";
+import { MouseoverModule } from "src/mouseoverModule";
+import { StatusCardComponent } from "./statusCard/statusCard.component";
+import { ShareModule } from "src/share";
+import { FormsModule, ReactiveFormsModule } from "@angular/forms";
+import { BehaviorSubject } from "rxjs";
+import { StateModule } from "src/state";
+import { AuthModule } from "src/auth";
+import {QuickTourModule} from "src/ui/quickTour/module";
+import { WindowResizeModule } from "src/util/windowResize";
+import { ViewerCtrlModule } from "./viewerCtrl";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    FormsModule,
+    ReactiveFormsModule,
+    UtilModule,
+    LayoutModule,
+    AngularMaterialModule,
+    Landmark2DModule,
+    ComponentsModule,
+    MouseoverModule,
+    ShareModule,
+    WindowResizeModule,
+    ViewerCtrlModule,
+
+    /**
+     * should probably break this into its own...
+     * share url module or something?
+     */
+    StateModule,
+    AuthModule,
+    StoreModule.forFeature(
+      NEHUBA_VIEWER_FEATURE_KEY,
+      reducer
+    ),
+    QuickTourModule
+  ],
+  declarations: [
+    NehubaViewerContainerDirective,
+    NehubaViewerUnit,
+    NehubaViewerTouchDirective,
+    NehubaGlueCmp,
+    TouchSideClass,
+    MaximisePanelButton,
+    StatusCardComponent,
+  ],
+  exports: [
+    NehubaViewerUnit,
+    NehubaViewerTouchDirective,
+    NehubaGlueCmp,
+    StatusCardComponent,
+  ],
+  providers: [
+    {
+      provide: IMPORT_NEHUBA_INJECT_TOKEN,
+      useFactory: importNehubaFactory,
+      deps: [ APPEND_SCRIPT_TOKEN ]
+    },
+    {
+      provide: NEHUBA_INSTANCE_INJTKN,
+      useValue: new BehaviorSubject(null)
+    }
+  ]
+})
+
+export class NehubaModule{}
diff --git a/src/viewerModule/nehuba/navigation.service/index.ts b/src/viewerModule/nehuba/navigation.service/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ac47740ee69a641d7ef23ed05e27eeb80a945824
--- /dev/null
+++ b/src/viewerModule/nehuba/navigation.service/index.ts
@@ -0,0 +1,7 @@
+export {
+  NehubaNavigationService
+} from './navigation.service'
+
+export {
+  INavObj
+} from './navigation.util'
diff --git a/src/viewerModule/nehuba/navigation.service/navigation.service.spec.ts b/src/viewerModule/nehuba/navigation.service/navigation.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9fdde1ebbab0cd2c3a8d71c01d935d9b175b897c
--- /dev/null
+++ b/src/viewerModule/nehuba/navigation.service/navigation.service.spec.ts
@@ -0,0 +1,171 @@
+import { discardPeriodicTasks, fakeAsync, TestBed, tick } from '@angular/core/testing'
+import { MockStore, provideMockStore } from '@ngrx/store/testing'
+import { BehaviorSubject, of, Subject } from 'rxjs'
+import { selectViewerConfigAnimationFlag } from 'src/services/state/viewerConfig/selectors'
+import { viewerStateSelectorNavigation } from 'src/services/state/viewerState/selectors'
+import * as NavUtil from './navigation.util'
+import { NehubaViewerUnit } from '../nehubaViewer/nehubaViewer.component'
+import { NEHUBA_INSTANCE_INJTKN } from '../util'
+import { NehubaNavigationService } from './navigation.service'
+
+const nav1 = {
+  position: [1,2,3],
+  orientation: [0, 0, 0, 1],
+  perspectiveOrientation: [1, 0, 0, 0],
+  perspectiveZoom: 100,
+  zoom: -12
+}
+
+const nav1x2 = {
+  position: [2,4,6],
+  orientation: [0, 0, 0, 2],
+  perspectiveOrientation: [2, 0, 0, 0],
+  perspectiveZoom: 200,
+  zoom: -24
+}
+
+const nav2 = {
+  position: [5, 1, -3],
+  orientation: [0, 0, 1, 0],
+  perspectiveOrientation: [-3, 0, 0, 0],
+  perspectiveZoom: 150,
+  zoom: -60
+}
+
+const nav1p2 = {
+  position: [6, 3, 0],
+  orientation: [0, 0, 1, 1],
+  perspectiveOrientation: [-2, 0, 0, 0],
+  perspectiveZoom: 250,
+  zoom: -72
+}
+describe('> navigation.service.ts', () => {
+
+  describe('> NehubaNavigationService', () => {
+    let nehubaInst$: BehaviorSubject<NehubaViewerUnit>
+    let nehubaInst: Partial<NehubaViewerUnit>
+    let service: NehubaNavigationService
+    beforeEach(() => {
+      nehubaInst$ = new BehaviorSubject(null)
+      TestBed.configureTestingModule({
+        imports: [
+
+        ],
+        providers: [
+          provideMockStore(),
+          {
+            provide: NEHUBA_INSTANCE_INJTKN,
+            useValue: nehubaInst$
+          },
+          NehubaNavigationService
+        ]
+      })
+
+      const mockStore = TestBed.inject(MockStore)
+      mockStore.overrideSelector(
+        viewerStateSelectorNavigation,
+        nav1
+      )
+      mockStore.overrideSelector(
+        selectViewerConfigAnimationFlag,
+        true
+      )
+    })
+  
+    it('> on new emit null on nehubaInst, clearViewSub is called, but setupviewersub is not called', () => {
+
+      service = TestBed.inject(NehubaNavigationService)
+      const clearviewSpy = spyOn(service, 'clearViewerSub').and.callThrough()
+      const setupViewSpy = spyOn(service, 'setupViewerSub').and.callThrough()
+      nehubaInst$.next(null)
+      expect(clearviewSpy).toHaveBeenCalled()
+      expect(setupViewSpy).not.toHaveBeenCalled()
+    })
+
+    it('> on new emit with viewer, clear view sub and setupviewers are both called', () => {
+
+      service = TestBed.inject(NehubaNavigationService)
+      const clearviewSpy = spyOn(service, 'clearViewerSub').and.callThrough()
+      const setupViewSpy = spyOn(service, 'setupViewerSub').and.callThrough()
+      nehubaInst = {
+        viewerPositionChange: of(nav1) as any,
+        setNavigationState: jasmine.createSpy()
+      }
+      nehubaInst$.next(nehubaInst as NehubaViewerUnit)
+      expect(clearviewSpy).toHaveBeenCalled()
+      expect(setupViewSpy).toHaveBeenCalled()
+    })
+
+    describe('> #setupViewerSub', () => {
+      let dispatchSpy: jasmine.Spy
+      beforeEach(() => {
+        nehubaInst = {
+          viewerPositionChange: new Subject() as any,
+          setNavigationState: jasmine.createSpy(),
+        }
+
+        service = TestBed.inject(NehubaNavigationService)
+        service['nehubaViewerInstance'] = nehubaInst as NehubaViewerUnit
+
+        const mockStore = TestBed.inject(MockStore)
+        mockStore.overrideSelector(viewerStateSelectorNavigation, nav1)
+        dispatchSpy = spyOn(mockStore, 'dispatch').and.callFake(() => {})
+      })
+
+      describe('> on viewerPosition change multiple times', () => {
+        beforeEach(() => {
+          service.setupViewerSub()
+        })
+        it('> viewerNav set to last value', fakeAsync(() => {
+
+          nehubaInst.viewerPositionChange.next(nav2)
+          nehubaInst.viewerPositionChange.next(nav1x2)
+          expect(
+            service.viewerNav
+          ).toEqual(nav1x2 as any)
+          discardPeriodicTasks()
+        }))
+
+        it('> dispatch does not get called immediately', fakeAsync(() => {
+
+          nehubaInst.viewerPositionChange.next(nav2)
+          nehubaInst.viewerPositionChange.next(nav1x2)
+          expect(dispatchSpy).not.toHaveBeenCalled()
+          discardPeriodicTasks()
+        }))
+
+        it('> dispatch called after 160 debounce', fakeAsync(() => {
+          
+          // next/'ing cannot be done in beforeEach
+          // or this test will fail
+          nehubaInst.viewerPositionChange.next(nav2)
+          nehubaInst.viewerPositionChange.next(nav1x2)
+          tick(160)
+          expect(dispatchSpy).toHaveBeenCalled()
+        }))
+      })
+    })
+  
+    describe('> on storeNavigation update', () => {
+      let navEqlSpy: jasmine.Spy
+      beforeEach(() => {
+        nehubaInst = {
+          setNavigationState: jasmine.createSpy(),
+          viewerPositionChange: new Subject() as any,
+        }
+        nehubaInst$.next(nehubaInst as NehubaViewerUnit)
+        navEqlSpy = spyOnProperty(NavUtil, 'navObjEqual')
+      })
+      it('> if navEq returnt true, do not setNav', () => {
+        navEqlSpy.and.returnValue(() => true)
+        service = TestBed.inject(NehubaNavigationService)
+        expect(nehubaInst.setNavigationState).not.toHaveBeenCalled()
+      })
+      it('> if navEq return false, call setNav', () => {
+        navEqlSpy.and.returnValue(() => false)
+        service = TestBed.inject(NehubaNavigationService)
+        expect(nehubaInst.setNavigationState).toHaveBeenCalled()
+      })
+    })
+  })
+})
diff --git a/src/viewerModule/nehuba/navigation.service/navigation.service.ts b/src/viewerModule/nehuba/navigation.service/navigation.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ed751e8b85149abb089fbd5cf5adf0ec888abdee
--- /dev/null
+++ b/src/viewerModule/nehuba/navigation.service/navigation.service.ts
@@ -0,0 +1,154 @@
+import { Inject, Injectable, OnDestroy, Optional } from "@angular/core";
+import { select, Store } from "@ngrx/store";
+import { Observable, ReplaySubject, Subscription } from "rxjs";
+import { debounceTime } from "rxjs/operators";
+import { selectViewerConfigAnimationFlag } from "src/services/state/viewerConfig/selectors";
+import { viewerStateChangeNavigation } from "src/services/state/viewerState/actions";
+import { viewerStateSelectorNavigation } from "src/services/state/viewerState/selectors";
+import { NehubaViewerUnit } from "../nehubaViewer/nehubaViewer.component";
+import { NEHUBA_INSTANCE_INJTKN } from "../util";
+import { timedValues } from 'src/util/generator'
+import { INavObj, navAdd, navMul, navObjEqual } from './navigation.util'
+
+@Injectable()
+export class NehubaNavigationService implements OnDestroy{
+
+  private subscriptions: Subscription[] = []
+  private viewerInstanceSubscriptions: Subscription[] = []
+
+  private nehubaViewerInstance: NehubaViewerUnit
+  public storeNav: INavObj
+  public viewerNav: INavObj
+  public viewerNav$ = new ReplaySubject<INavObj>(1)
+
+  // if set, ignores store attempt to update nav
+  private viewerNavLock: boolean = false
+
+  private globalAnimationFlag = true
+  private rafRef: number
+
+  constructor(
+    private store$: Store<any>,
+    @Optional() @Inject(NEHUBA_INSTANCE_INJTKN) nehubaInst$: Observable<NehubaViewerUnit>,
+  ){
+    this.subscriptions.push(
+      this.store$.pipe(
+        select(selectViewerConfigAnimationFlag)
+      ).subscribe(flag => this.globalAnimationFlag = flag)
+    )
+
+    if (nehubaInst$) {
+      this.subscriptions.push(
+        nehubaInst$.subscribe(val => {
+          this.clearViewerSub()
+          this.nehubaViewerInstance = val
+          if (this.nehubaViewerInstance) {
+            this.setupViewerSub()
+          }
+        })
+      )
+    }
+
+    this.subscriptions.push(
+      // realtime state nav state
+      this.store$.pipe(
+        select(viewerStateSelectorNavigation)
+      ).subscribe(v => {
+        this.storeNav = v
+        // if stored nav differs from viewerNav
+        if (!this.viewerNavLock && this.nehubaViewerInstance) {
+          const navEql = navObjEqual(this.storeNav, this.viewerNav)
+          if (!navEql) {
+            this.navigateViewer({
+              ...this.storeNav,
+              positionReal: true
+            })
+          }
+        }
+      })
+    )
+  }
+
+  navigateViewer(navigation: INavObj & { positionReal?: boolean, animation?: any }){
+    if (!navigation) return
+    const { animation, ...rest } = navigation
+    if (animation && this.globalAnimationFlag) {
+
+      const gen = timedValues()
+      const src = this.viewerNav
+
+      const dest = {
+        ...src,
+        ...navigation
+      }
+
+      const delta = navAdd(dest, navMul(src, -1))
+
+      const animate = () => {
+        const next = gen.next()
+        const d =  next.value
+
+        const n = navAdd(src, navMul(delta, d))
+        this.nehubaViewerInstance.setNavigationState({
+          ...n,
+          positionReal: true
+        })
+
+        if ( !next.done ) {
+          this.rafRef = requestAnimationFrame(() => animate())
+        }
+      }
+      this.rafRef = requestAnimationFrame(() => animate())
+    } else {
+      this.nehubaViewerInstance.setNavigationState(rest)
+    }
+  }
+
+  setupViewerSub(){
+    this.viewerInstanceSubscriptions.push(
+      // realtime viewer nav state
+      this.nehubaViewerInstance.viewerPositionChange.subscribe(
+        (val: INavObj) => {
+          this.viewerNav = val
+          this.viewerNav$.next(val)
+          this.viewerNavLock = true
+        }
+      ),
+      // debounced viewer nav state
+      this.nehubaViewerInstance.viewerPositionChange.pipe(
+        debounceTime(160)
+      ).subscribe((val: INavObj) => {
+        this.viewerNavLock = false
+
+        const { zoom, perspectiveZoom, position } = val
+        const roundedZoom = Math.round(zoom)
+        const roundedPz = Math.round(perspectiveZoom)
+        const roundedPosition = position.map(v => Math.round(v)) as [number, number, number]
+        const roundedNav = {
+          ...val,
+          zoom: roundedZoom,
+          perspectiveZoom: roundedPz,
+          position: roundedPosition,
+        }
+        const navEql = navObjEqual(roundedNav, this.storeNav)
+        
+        if (!navEql) {
+          this.store$.dispatch(
+            viewerStateChangeNavigation({
+              navigation: roundedNav
+            })
+          )
+        }
+      })
+    )
+  }
+
+  clearViewerSub(){
+    while (this.viewerInstanceSubscriptions.length > 0) this.viewerInstanceSubscriptions.pop().unsubscribe()
+  }
+
+  ngOnDestroy(){
+    this.clearViewerSub()
+    while (this.subscriptions.length > 0) this.subscriptions.pop().unsubscribe()
+  }
+}
diff --git a/src/viewerModule/nehuba/navigation.service/navigation.util.spec.ts b/src/viewerModule/nehuba/navigation.service/navigation.util.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8cd54f17460859f88ff71ce7d2f7c0c1d779be2a
--- /dev/null
+++ b/src/viewerModule/nehuba/navigation.service/navigation.util.spec.ts
@@ -0,0 +1,112 @@
+import {
+  navAdd,
+  navMul,
+  navObjEqual,
+  INavObj
+} from './navigation.util'
+
+const nav1: INavObj = {
+  position: [1,2,3],
+  orientation: [0, 0, 0, 1],
+  perspectiveOrientation: [1, 0, 0, 0],
+  perspectiveZoom: 100,
+  zoom: -12
+}
+
+const nav1x2: INavObj = {
+  position: [2,4,6],
+  orientation: [0, 0, 0, 2],
+  perspectiveOrientation: [2, 0, 0, 0],
+  perspectiveZoom: 200,
+  zoom: -24
+}
+
+const nav2: INavObj = {
+  position: [5, 1, -3],
+  orientation: [0, 0, 1, 0],
+  perspectiveOrientation: [-3, 0, 0, 0],
+  perspectiveZoom: 150,
+  zoom: -60
+}
+
+const nav1p2: INavObj = {
+  position: [6, 3, 0],
+  orientation: [0, 0, 1, 1],
+  perspectiveOrientation: [-2, 0, 0, 0],
+  perspectiveZoom: 250,
+  zoom: -72
+}
+
+describe('> navigation.util.ts', () => {
+  describe('> navMul', () => {
+    it('> should multiply nav object with scalar', () => {
+      expect(
+        navMul(nav1x2, 0.5)
+      ).toEqual(nav1)
+    })
+  })
+
+  describe('> navAdd', () => {
+    it('> should add two nav obj', () => {
+      expect(
+        navAdd(nav1, nav2)
+      ).toEqual(nav1p2)
+    })
+  })
+
+  describe('> navObjEqual', () => {
+    describe('> if inputs are strictly equal', () => {
+      it('> if two objects are inav obj', () => {
+        expect(
+          navObjEqual(nav1, nav1)
+        ).toBeTrue()
+      })
+      it('> if two objects are both null', () => {
+        expect(
+          navObjEqual(null, null)
+        ).toBeTrue()
+      })
+      it('> if two objects are both undefined', () => {
+        const obj = {}
+        expect(
+          navObjEqual(obj['plus'], obj['bla'])
+        ).toBeTrue()
+      })
+    })
+
+    describe('> if inputs are not strictly eqal', () => {
+      describe('> if either inputs are falsy', () => {
+        it('> if the other argument is falsy, return false', () => {
+          const obj = {}
+          expect(
+            navObjEqual(obj['plug'], null)
+          ).toBeFalse()
+        })
+
+        it('> if the other argument is valid, returns false', () => {
+          expect(
+            navObjEqual(null, nav1)
+          ).toBeFalse()
+        })
+      })
+    
+      describe('> if both inputs are valid obj', () => {
+        it('> should return false if different value', () => {
+          expect(
+            navObjEqual(nav1, nav2)
+          ).toBeFalse()
+        })
+
+        it('> should return true if same value', () => {
+          expect(
+            navObjEqual(
+              navMul(nav1, 2),
+              nav1x2
+            )
+          ).toBeTrue()
+        })
+      })
+    })
+  })
+
+})
\ No newline at end of file
diff --git a/src/viewerModule/nehuba/navigation.service/navigation.util.ts b/src/viewerModule/nehuba/navigation.service/navigation.util.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9ae2df4760f4216b0e9f98e4958750882b791c21
--- /dev/null
+++ b/src/viewerModule/nehuba/navigation.service/navigation.util.ts
@@ -0,0 +1,58 @@
+import { TVec3, TVec4 } from "src/messaging/types";
+import { arrayOfPrimitiveEqual } from "src/util/fn";
+
+export interface INavObj {
+  position: TVec3
+  orientation: TVec4
+  perspectiveOrientation: TVec4
+  perspectiveZoom: number
+  zoom: number
+}
+
+export function navMul(nav: INavObj, scalar: number): INavObj {
+  return {
+    zoom: nav.zoom * scalar,
+    perspectiveZoom: nav.perspectiveZoom * scalar,
+    position: nav.position.map(v => v * scalar) as TVec3,
+    orientation: nav.orientation.map(v => v * scalar) as TVec4,
+    perspectiveOrientation: nav.perspectiveOrientation.map(v => v * scalar) as TVec4,
+  }
+}
+
+export function navAdd(nav1: INavObj, nav2: INavObj): INavObj {
+  return {
+    zoom: nav1.zoom + nav2.zoom,
+    perspectiveZoom: nav1.perspectiveZoom + nav2.perspectiveZoom,
+    position: [0, 1, 2].map(idx => nav1.position[idx] + nav2.position[idx]) as [number, number, number],
+    orientation: [0, 1, 2, 3].map(idx => nav1.orientation[idx] + nav2.orientation[idx]) as [number, number, number, number],
+    perspectiveOrientation: [0, 1, 2, 3].map(idx => nav1.perspectiveOrientation[idx] + nav2.perspectiveOrientation[idx]) as [number, number, number, number],
+  }
+}
+
+export function navObjEqual(nav1: INavObj, nav2: INavObj): boolean{
+  if (nav1 === nav2) return true
+  if (!nav1) return false
+  if (!nav2) return false
+  
+  const keys = ["orientation", "perspectiveOrientation", "perspectiveZoom", "position", "zoom"]
+  let flag: boolean = true
+  for (const key of keys) {
+    if (typeof nav1[key] !== typeof nav2[key]) {
+      flag = false
+      break
+    }
+    if (typeof nav1[key] === 'number') {
+      if (Math.round(nav1[key]) !== Math.round(nav2[key])) {
+        flag = false
+        break
+      }
+    } else {
+      const _flag = arrayOfPrimitiveEqual(nav1[key], nav2[key])
+      if (!_flag) {
+        flag = false
+        break
+      }
+    }
+  }
+  return flag
+}
\ No newline at end of file
diff --git a/src/viewerModule/nehuba/nehubaContainer.component.spec.ts b/src/viewerModule/nehuba/nehubaContainer.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..61bb83b5671bbfe3366c0a5ae2450bfa0f4c5711
--- /dev/null
+++ b/src/viewerModule/nehuba/nehubaContainer.component.spec.ts
@@ -0,0 +1,688 @@
+
+// const { 
+//   TOGGLE_SIDE_PANEL,
+//   EXPAND,
+//   COLLAPSE,
+//   ZOOM_IN,
+//   ZOOM_OUT,
+//   TOGGLE_FRONTAL_OCTANT
+// } = ARIA_LABELS
+
+// const _bigbrainJson = require('!json-loader!src/res/ext/bigbrain.json')
+// const _bigbrainNehubaConfigJson = require('!json-loader!src/res/ext/bigbrainNehubaConfig.json')
+// const bigbrainJson = {
+//   ..._bigbrainJson,
+//   nehubaConfig: _bigbrainNehubaConfigJson
+// }
+// const humanAtlas = require('!json-loader!src/res/ext/atlas/atlas_multiLevelHuman.json')
+// const importNehubaSpy = jasmine.createSpy('importNehubaSpy').and.returnValue(Promise.reject())
+
+describe('> nehubaContainer.component.ts', () => {
+
+  describe('> NehubaContainer', () => {
+
+    // beforeEach(async(() => {
+
+    //   TestBed.configureTestingModule({
+    //     imports: [
+    //       NoopAnimationsModule,
+    //       WidgetModule,
+    //       AngularMaterialModule,
+    //       LayoutModule,
+    //       UtilModule,
+    //       DatabrowserModule,
+    //       NehubaModule,
+    //       AuthModule,
+    //       StateModule,
+    //       FormsModule,
+    //       ReactiveFormsModule,
+    //       HttpClientModule,
+    //       CommonModule,
+    //       RegionalFeaturesModule,
+    //       ParcellationRegionModule,
+    //       AtlasCmpParcellationModule,
+
+    //       /**
+    //        * because the change done to pureconstant service, need to intercept http call to avoid crypto error message
+    //        * so and so components needs to be compiled first. make sure you call compileComponents
+    //        */
+    //       HttpClientTestingModule,
+    //       Landmark2DModule,
+    //     ],
+    //     declarations: [
+    //       NehubaContainer,
+    //       TouchSideClass,
+    //       MaximisePanelButton,
+    //       AtlasLayerSelector,
+    //       StatusCardComponent,
+    //       NehubaViewerTouchDirective,
+    //       MobileOverlay,
+          
+    //       SplashScreen,
+    //       CurrentLayout,
+  
+    //       // pipes
+    //       MobileControlNubStylePipe,
+    //       ReorderPanelIndexPipe,
+          
+    //       RegionAccordionTooltipTextPipe,
+    //     ],
+    //     providers: [
+    //       provideMockStore({ initialState: defaultRootState }),
+    //       {
+    //         provide: IMPORT_NEHUBA_INJECT_TOKEN,
+    //         useValue: importNehubaSpy
+    //       },
+    //       PureContantService,
+    //     ],
+    //     schemas: [
+    //       CUSTOM_ELEMENTS_SCHEMA
+    //     ],
+    //   }).compileComponents()
+      
+    // }))
+
+    
+    // const fixture = TestBed.createComponent(NehubaContainer)
+    // fixture.componentInstance.currentOnHoverObs$ = hot('')
+    // const el = fixture.debugElement.componentInstance
+    // expect(el).toBeTruthy()
+    it('> component can be created')
+
+    describe('> on selectedTemplatechange', () => {
+
+      // const fixture = TestBed.createComponent(NehubaContainer)
+      // fixture.componentInstance.currentOnHoverObs$ = hot('')
+
+      // const mockStore = TestBed.inject(MockStore)
+      // const newState = {
+      //   ...defaultRootState,
+      //   viewerState: {
+      //     ...defaultRootState.viewerState,
+      //     fetchedTemplates: [ bigbrainJson ],
+      //     templateSelected: bigbrainJson,
+      //     parcellationSelected: bigbrainJson.parcellations[0]
+      //   },
+      //   [viewerStateHelperStoreName]: {
+      //     fetchedAtlases: [ humanAtlas ],
+      //     selectedAtlasId: humanAtlas['@id']
+      //   }
+      // }
+
+      // mockStore.setState(newState)
+      // fixture.detectChanges()
+      // expect(importNehubaSpy).toHaveBeenCalled()
+      it('> calls importNehubaPr')
+
+      /**
+       * TODO perhaps move this to e2e?
+       */
+      it('> drag handle reattaches properly')
+    })
+
+    describe('> on selectedparcellation change', () => {
+
+
+      // const fixture = TestBed.createComponent(NehubaContainer)
+      // fixture.componentInstance.currentOnHoverObs$ = hot('')
+      // const el = fixture.debugElement.componentInstance as NehubaContainer
+      // const mockStore = TestBed.inject(MockStore)
+      // const newState = {
+      //   ...defaultRootState,
+      //   viewerState: {
+      //     ...defaultRootState.viewerState,
+      //     fetchedTemplates: [ bigbrainJson ],
+      //     templateSelected: bigbrainJson,
+      //     parcellationSelected: bigbrainJson.parcellations[0]
+      //   },
+      //   [viewerStateHelperStoreName]: {
+      //     fetchedAtlases: [ humanAtlas ],
+      //     selectedAtlasId: humanAtlas['@id']
+      //   }
+      // }
+
+      // mockStore.setState(newState)
+      // fixture.detectChanges()
+
+      // const setSpy = spyOnProperty(el.nehubaViewer, 'ngIds', 'set')
+
+      // const newState2 = {
+      //   ...defaultRootState,
+      //   viewerState: {
+      //     ...defaultRootState.viewerState,
+      //     fetchedTemplates: [ bigbrainJson ],
+      //     templateSelected: bigbrainJson,
+      //     parcellationSelected: bigbrainJson.parcellations[1]
+      //   },
+      //   [viewerStateHelperStoreName]: {
+      //     fetchedAtlases: [ humanAtlas ],
+      //     selectedAtlasId: humanAtlas['@id']
+      //   }
+      // }
+
+      // mockStore.setState(newState2)
+      // fixture.detectChanges()
+
+      // expect(setSpy).toHaveBeenCalled()
+      it('> should set ngId of nehubaViewer')
+    })
+
+    describe('> extended sidepanel hides and shows as expected', () => {
+      describe('> on start, if nothing is selected', () => {
+        // beforeEach(() => {
+        //   const mockStore = TestBed.inject(MockStore)
+        //   const newState = {
+        //     ...defaultRootState,
+        //     viewerState: {
+        //       ...defaultRootState.viewerState,
+        //       fetchedTemplates: [ bigbrainJson ],
+        //       templateSelected: bigbrainJson,
+        //       parcellationSelected: bigbrainJson.parcellations[0]
+        //     },
+        //     [viewerStateHelperStoreName]: {
+        //       fetchedAtlases: [ humanAtlas ],
+        //       selectedAtlasId: humanAtlas['@id']
+        //     }
+        //   }
+
+        //   mockStore.setState(newState)
+        // })
+
+
+
+        // const fixture = TestBed.createComponent(NehubaContainer)
+        // fixture.componentInstance.currentOnHoverObs$ = hot('')
+        // fixture.detectChanges()
+        // expect(
+        //   fixture.componentInstance.matDrawerMain.opened
+        // ).toEqual(false)
+        // expect(
+        //   fixture.componentInstance.matDrawerMinor.opened
+        // ).toEqual(false)
+        it('> both should be shut')
+
+
+        // const fixture = TestBed.createComponent(NehubaContainer)
+        // fixture.componentInstance.currentOnHoverObs$ = hot('')
+        // fixture.detectChanges()
+        // const toggleBtn = fixture.debugElement.query( By.css(`[aria-label="${TOGGLE_SIDE_PANEL}"]`) )
+        // toggleBtn.triggerEventHandler('click', null)
+        // fixture.detectChanges()
+        
+        // expect(
+        //   fixture.componentInstance.matDrawerMain.opened
+        // ).toEqual(true)
+        // expect(
+        //   fixture.componentInstance.matDrawerMinor.opened
+        // ).toEqual(false)
+        it('> opening via tab should result in only top drawer open')
+
+
+
+        // const fixture = TestBed.createComponent(NehubaContainer)
+        // fixture.componentInstance.currentOnHoverObs$ = hot('')
+        // fixture.detectChanges()
+        // const toggleBtn = fixture.debugElement.query( By.css(`[aria-label="${TOGGLE_SIDE_PANEL}"]`) )
+        // toggleBtn.triggerEventHandler('click', null)
+        // fixture.detectChanges()
+        // const expandRegionFeatureBtn = fixture.debugElement.query( By.css(`mat-drawer[data-mat-drawer-secondary-open="true"] [aria-label="${EXPAND}"]`) )
+        // expect(expandRegionFeatureBtn).toBeNull()
+        it('> on opening top drawer, explore features should not be present')
+
+
+        // const fixture = TestBed.createComponent(NehubaContainer)
+        // fixture.componentInstance.currentOnHoverObs$ = hot('')
+        // fixture.detectChanges()
+        // const toggleBtn = fixture.debugElement.query( By.css(`[aria-label="${TOGGLE_SIDE_PANEL}"]`) )
+        // toggleBtn.triggerEventHandler('click', null)
+        // fixture.detectChanges()
+        // const expandRegionFeatureBtn = fixture.debugElement.query( By.css(`mat-drawer[data-mat-drawer-secondary-open="true"] [aria-label="${COLLAPSE}"]`) )
+        // expect(expandRegionFeatureBtn).toBeNull()
+        it('> collapse btn should not be visible')
+      })
+
+      describe('> on start, if something is selected', () => {
+        // beforeEach(() => {
+        //   const mockStore = TestBed.inject(MockStore)
+        //   const newState = {
+        //     ...defaultRootState,
+        //     viewerState: {
+        //       ...defaultRootState.viewerState,
+        //       fetchedTemplates: [ bigbrainJson ],
+        //       templateSelected: bigbrainJson,
+        //       parcellationSelected: bigbrainJson.parcellations[0],
+        //       regionsSelected: [{
+        //         name: "foobar",
+        //         ngId: 'untitled',
+        //         labelIndex: 15
+        //       }]
+        //     },
+        //     [viewerStateHelperStoreName]: {
+        //       fetchedAtlases: [ humanAtlas ],
+        //       selectedAtlasId: humanAtlas['@id']
+        //     }
+        //   }
+
+        //   mockStore.setState(newState)
+        // })
+
+
+        // const fixture = TestBed.createComponent(NehubaContainer)
+        // fixture.componentInstance.currentOnHoverObs$ = hot('')
+        // fixture.detectChanges()
+        // expect(
+        //   fixture.componentInstance.matDrawerMain.opened
+        // ).toEqual(true)
+        // expect(
+        //   fixture.componentInstance.matDrawerMinor.opened
+        // ).toEqual(true)
+
+        // expect(
+        //   fixture.componentInstance.navSideDrawerMainSwitch.switchState
+        // ).toEqual(true)
+        // expect(
+        //   fixture.componentInstance.navSideDrawerMinorSwitch.switchState
+        // ).toEqual(true)
+        it('> both should be open')
+
+
+        // () => {
+        //   const fixture = TestBed.createComponent(NehubaContainer)
+        //   fixture.componentInstance.currentOnHoverObs$ = hot('')
+        //   fixture.detectChanges()
+        //   const toggleBtn = fixture.debugElement.query( By.css(`[aria-label="${TOGGLE_SIDE_PANEL}"]`) )
+        //   toggleBtn.triggerEventHandler('click', null)
+        //   fixture.detectChanges()
+        //   expect(
+        //     fixture.componentInstance.matDrawerMain.opened
+        //   ).toEqual(false)
+
+        //   /**
+        //    * TODO investigate why openedStart/closedStart events fail to fire
+        //    */
+        //   // expect(
+        //   //   fixture.componentInstance.matDrawerMinor.opened
+        //   // ).toEqual(false)
+
+        //   // expect(
+        //   //   fixture.componentInstance.navSideDrawerMainSwitch.switchState
+        //   // ).toEqual(false)
+        //   // expect(
+        //   //   fixture.componentInstance.navSideDrawerMinorSwitch.switchState
+        //   // ).toEqual(false)
+        // }
+        it('> closing main drawer via tag should close both')
+
+
+        // () => {
+
+        //   const fixture = TestBed.createComponent(NehubaContainer)
+        //   fixture.componentInstance.currentOnHoverObs$ = hot('')
+        //   fixture.detectChanges()
+        //   const collapseRegionFeatureBtn = fixture.debugElement.query( By.css(`mat-drawer[data-mat-drawer-secondary-open="true"] [aria-label="${COLLAPSE}"]`) )
+        //   expect(collapseRegionFeatureBtn).not.toBeNull()
+        // }
+        it('> collapse btn should be visible')
+
+        // () => {
+
+        //   const fixture = TestBed.createComponent(NehubaContainer)
+        //   fixture.componentInstance.currentOnHoverObs$ = hot('')
+        //   fixture.detectChanges()
+        //   const collapseRegionFeatureBtn = fixture.debugElement.query( By.css(`mat-drawer[data-mat-drawer-secondary-open="true"] [aria-label="${COLLAPSE}"]`) )
+        //   collapseRegionFeatureBtn.triggerEventHandler('click', null)
+        //   fixture.detectChanges()
+        //   expect(
+        //     fixture.componentInstance.matDrawerMain.opened
+        //   ).toEqual(true)
+
+        //   /**
+        //    * TODO investigate why property does not get updated
+        //    */
+        //   // expect(
+        //   //   fixture.componentInstance.matDrawerMinor.opened
+        //   // ).toEqual(false)
+
+        //   expect(
+        //     fixture.componentInstance.navSideDrawerMainSwitch.switchState
+        //   ).toEqual(true)
+        //   expect(
+        //     fixture.componentInstance.navSideDrawerMinorSwitch.switchState
+        //   ).toEqual(false)
+        // }
+        it('> clicking on collapse btn should minimize 1 drawer')
+
+        // () => {
+        //   const fixture = TestBed.createComponent(NehubaContainer)
+        //   fixture.componentInstance.currentOnHoverObs$ = hot('')
+        //   fixture.detectChanges()
+        //   const collapseRegionFeatureBtn = fixture.debugElement.query( By.css(`mat-drawer[data-mat-drawer-secondary-open="true"] [aria-label="${COLLAPSE}"]`) )
+        //   collapseRegionFeatureBtn.triggerEventHandler('click', null)
+        //   fixture.detectChanges()
+        //   const expandRegionFeatureBtn = fixture.debugElement.query( By.css(`mat-drawer[data-mat-drawer-primary-open="true"] [aria-label="${EXPAND}"]`) )
+        //   expandRegionFeatureBtn.triggerEventHandler('click', null)
+        //   fixture.detectChanges()
+
+        //   expect(
+        //     fixture.componentInstance.matDrawerMain.opened
+        //   ).toEqual(true)
+        //   expect(
+        //     fixture.componentInstance.matDrawerMinor.opened
+        //   ).toEqual(true)
+
+        //   expect(
+        //     fixture.componentInstance.navSideDrawerMainSwitch.switchState
+        //   ).toEqual(true)
+        //   /**
+        //    * TODO figoure out why switch state is updated async, and karma can't force update state
+        //    */
+        //   // expect(
+        //   //   fixture.componentInstance.navSideDrawerMinorSwitch.switchState
+        //   // ).toEqual(true)
+        // }
+        it('> on minimize drawer, clicking expand btn should expand everything')
+      })
+
+      describe('> side bar content', () => {
+
+        /**
+         * TODO
+         */
+        it('> if nothing is shown, it should show place holder text')
+
+        /**
+         * TODO
+         */
+        it('> if something (region features/connectivity) exists, placeh holder text should be hdiden')
+      })
+    })
+  
+    describe('> panelCtrl', () => {
+      // let fixture: ComponentFixture<NehubaContainer>
+      // const setViewerLoaded = () => {
+      //   fixture.componentInstance.viewerLoaded = true
+      // }
+      // const ctrlElementIsVisible = (el: DebugElement) => {
+      //   const visible = (el.nativeElement as HTMLElement).getAttribute('data-viewer-controller-visible')
+      //   return visible === 'true'
+      // }
+      // beforeEach(() => {
+      //   fixture = TestBed.createComponent(NehubaContainer)
+      // })
+
+      // () => {
+      //   fixture.detectChanges()
+      //   setViewerLoaded()
+      //   fixture.detectChanges()
+      //   for (const idx of [0, 1, 2, 3]) {
+      //     const el = fixture.debugElement.query(
+      //       By.css(`[data-viewer-controller-index="${idx}"]`)
+      //     )
+      //     expect(el).toBeTruthy()
+      //   }
+      // }
+      it('> on start, all four ctrl panels exists')
+
+      // () => {
+        
+      //   fixture.detectChanges()
+      //   setViewerLoaded()
+      //   fixture.detectChanges()
+      //   for (const idx of [0, 1, 2, 3]) {
+      //     const el = fixture.debugElement.query(
+      //       By.css(`[data-viewer-controller-index="${idx}"]`)
+      //     )
+      //     expect(ctrlElementIsVisible(el)).toBeFalsy()
+      //   }
+      // }
+      it('> on start all four ctrl panels are invisible')
+
+      describe('> on hover, only the hovered panel have ctrl shown', () => {
+
+        for (const idx of [0, 1, 2, 3]) {
+          
+          // fakeAsync(() => {
+          //   fixture.detectChanges()
+          //   const findPanelIndexSpy = spyOn<any>(fixture.componentInstance, 'findPanelIndex').and.callFake(() => {
+          //     return idx
+          //   })
+          //   setViewerLoaded()
+          //   fixture.detectChanges()
+          //   const nativeElement = fixture.componentInstance['elementRef'].nativeElement
+          //   nativeElement.dispatchEvent(new MouseEvent('mouseover', { bubbles: true }))
+  
+          //   /**
+          //    * assert findPanelIndex called with event.target, i.e. native element in thsi case
+          //    */
+          //   expect(findPanelIndexSpy).toHaveBeenCalledWith(nativeElement)
+          //   tick(200)
+          //   fixture.detectChanges()
+            
+          //   /**
+          //    * every panel index should be non visible
+          //    * only when idx matches, it can be visible
+          //    * n.b. this does not test visual visibility (which is controlled by extra-style.css)
+          //    * (which is also affected by global [ismobile] configuration)
+          //    * 
+          //    * this merely test the unit logic, and sets the flag appropriately
+          //    */
+          //   for (const iterativeIdx of [0, 1, 2, 3]) {
+          //     const el = fixture.debugElement.query(
+          //       By.css(`[data-viewer-controller-index="${iterativeIdx}"]`)
+          //     )
+          //     if (iterativeIdx === idx) {
+          //       expect(ctrlElementIsVisible(el)).toBeTruthy()
+          //     } else {
+          //       expect(ctrlElementIsVisible(el)).toBeFalsy()
+          //     }
+          //   }
+          //   discardPeriodicTasks()
+          // })
+          it(`> on hoveredPanelIndices$ emit ${idx}, the panel index ${idx} ctrl becomes visible`)
+        }
+  
+      })
+
+      describe('> on maximise top right slice panel (idx 1)', () => {
+        // beforeEach(() => {
+        //   const mockStore = TestBed.inject(MockStore)
+        //   mockStore.overrideSelector(ngViewerSelectorPanelMode, PANELS.SINGLE_PANEL)
+        //   mockStore.overrideSelector(ngViewerSelectorPanelOrder, '1230')
+
+        //   fixture.detectChanges()
+        //   setViewerLoaded()
+        //   fixture.detectChanges()
+        // })
+
+
+        // () => {
+
+        //   const toggleBtn = fixture.debugElement.query(
+        //     By.css(`[cell-i] [aria-label="${TOGGLE_FRONTAL_OCTANT}"]`)
+        //   )
+        //   expect(toggleBtn).toBeFalsy()
+        // }
+        it('> toggle front octant btn not visible')
+
+        // () => {
+
+        //   const zoomInBtn = fixture.debugElement.query(
+        //     By.css(`[cell-i] [aria-label="${ZOOM_IN}"]`)
+        //   )
+
+        //   const zoomOutBtn = fixture.debugElement.query(
+        //     By.css(`[cell-i] [aria-label="${ZOOM_OUT}"]`)
+        //   )
+
+        //   expect(zoomInBtn).toBeTruthy()
+        //   expect(zoomOutBtn).toBeTruthy()
+        // }
+        it('> zoom in and out btns are visible')
+
+        // () => {
+        //   const zoomViewSpy = spyOn(fixture.componentInstance, 'zoomNgView')
+
+        //   const zoomInBtn = fixture.debugElement.query(
+        //     By.css(`[cell-i] [aria-label="${ZOOM_IN}"]`)
+        //   )
+        //   zoomInBtn.triggerEventHandler('click', null)
+        //   expect(zoomViewSpy).toHaveBeenCalled()
+        //   const { args } = zoomViewSpy.calls.first()
+        //   expect(args[0]).toEqual(1)
+        //   /**
+        //    * zoom in < 1
+        //    */
+        //   expect(args[1]).toBeLessThan(1)
+        // }
+        it('> zoom in btn calls fn with right param')
+
+        // () => {
+        //   const zoomViewSpy = spyOn(fixture.componentInstance, 'zoomNgView')
+
+        //   const zoomOutBtn = fixture.debugElement.query(
+        //     By.css(`[cell-i] [aria-label="${ZOOM_OUT}"]`)
+        //   )
+        //   zoomOutBtn.triggerEventHandler('click', null)
+        //   expect(zoomViewSpy).toHaveBeenCalled()
+        //   const { args } = zoomViewSpy.calls.first()
+        //   expect(args[0]).toEqual(1)
+        //   /**
+        //    * zoom out > 1
+        //    */
+        //   expect(args[1]).toBeGreaterThan(1)
+        // }
+        it('> zoom out btn calls fn with right param')
+      })
+
+      describe('> on maximise perspective panel', () => {
+        // beforeEach(() => {
+        //   const mockStore = TestBed.inject(MockStore)
+        //   mockStore.overrideSelector(ngViewerSelectorPanelMode, PANELS.SINGLE_PANEL)
+        //   mockStore.overrideSelector(ngViewerSelectorPanelOrder, '3012')
+
+        //   fixture.detectChanges()
+        //   setViewerLoaded()
+        //   fixture.detectChanges()
+        // })
+
+
+        // () => {
+        //   const setOctantRemovalSpy = spyOn(fixture.componentInstance, 'setOctantRemoval')
+
+        //   const toggleBtn = fixture.debugElement.query(
+        //     By.css(`[cell-i] [aria-label="${TOGGLE_FRONTAL_OCTANT}"]`)
+        //   )
+        //   expect(toggleBtn).toBeTruthy()
+        //   toggleBtn.nativeElement.dispatchEvent(
+        //     new MouseEvent('click', { bubbles: true })
+        //   )
+        //   expect(setOctantRemovalSpy).toHaveBeenCalled()
+        // }
+        it('> toggle octant btn visible and functional')
+
+        // () => {
+        //   const zoomViewSpy = spyOn(fixture.componentInstance, 'zoomNgView')
+
+        //   const zoomInBtn = fixture.debugElement.query(
+        //     By.css(`[cell-i] [aria-label="${ZOOM_IN}"]`)
+        //   )
+        //   expect(zoomInBtn).toBeTruthy()
+
+        //   zoomInBtn.triggerEventHandler('click', null)
+        //   expect(zoomViewSpy).toHaveBeenCalled()
+        //   const { args } = zoomViewSpy.calls.first()
+        //   expect(args[0]).toEqual(3)
+        //   /**
+        //    * zoom in < 1
+        //    */
+        //   expect(args[1]).toBeLessThan(1)
+        // }
+        it('> zoom in btn visible and functional')
+
+        // () => {
+        //   const zoomViewSpy = spyOn(fixture.componentInstance, 'zoomNgView')
+
+        //   const zoomOutBtn = fixture.debugElement.query(
+        //     By.css(`[cell-i] [aria-label="${ZOOM_OUT}"]`)
+        //   )
+        //   expect(zoomOutBtn).toBeTruthy()
+
+        //   zoomOutBtn.triggerEventHandler('click', null)
+        //   expect(zoomViewSpy).toHaveBeenCalled()
+        //   const { args } = zoomViewSpy.calls.first()
+        //   expect(args[0]).toEqual(3)
+        //   /**
+        //    * zoom in < 1
+        //    */
+        //   expect(args[1]).toBeGreaterThan(1)
+        // }
+        it('> zoom out btn visible and functional')
+      
+      })
+    })
+  
+    describe('> on userLandmarks change', () => {
+      const lm1 = {
+        id: 'test-1',
+        position: [0, 0, 0]
+      }
+      const lm2 = {
+        id: 'test-2',
+        position: [1, 1,1 ]
+      }
+
+      // () => {
+      //   const fixture = TestBed.createComponent(NehubaContainer)
+
+      //   fixture.componentInstance.nehubaViewer = {
+      //     updateUserLandmarks: () => {}
+      //   } as any
+
+      //   const updateUserLandmarksSpy = spyOn(
+      //     fixture.componentInstance.nehubaViewer,
+      //     'updateUserLandmarks'
+      //   )
+
+      //   const mockStore = TestBed.inject(MockStore)
+      //   mockStore.overrideSelector(viewerStateCustomLandmarkSelector, [
+      //     lm1, 
+      //     lm2
+      //   ])
+      //   fixture.detectChanges()
+      //   expect(
+      //     updateUserLandmarksSpy
+      //   ).toHaveBeenCalledWith([
+      //     lm1, lm2
+      //   ])
+      // }
+      it('> calls nehubaViewer.updateUserLandmarks')
+    
+      // () => {
+        
+      //   const fixture = TestBed.createComponent(NehubaContainer)
+
+      //   fixture.componentInstance.nehubaContainerDirective = {
+      //     toggleOctantRemoval: () => {},
+      //     clear: () => {}
+      //   } as any
+
+      //   const toggleOctantRemovalSpy = spyOn(
+      //     fixture.componentInstance.nehubaContainerDirective,
+      //     'toggleOctantRemoval'
+      //   )
+
+      //   const mockStore = TestBed.inject(MockStore)
+      //   mockStore.overrideSelector(viewerStateCustomLandmarkSelector, [
+      //     lm1, 
+      //     lm2
+      //   ])
+      //   mockStore.overrideSelector(ngViewerSelectorOctantRemoval, true)
+      //   fixture.detectChanges()
+      //   expect(
+      //     toggleOctantRemovalSpy
+      //   ).toHaveBeenCalledWith(false)
+      // }
+      it('> calls togglecotantREmoval')
+    })
+  })
+})
diff --git a/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.spec.ts b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f6b977efc20ceeccf1f729079e5308367a948bf5
--- /dev/null
+++ b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.spec.ts
@@ -0,0 +1,422 @@
+import { TestBed, async, fakeAsync, tick, ComponentFixture } from "@angular/core/testing"
+import { CommonModule, DOCUMENT } from "@angular/common"
+import { NehubaViewerUnit, IMPORT_NEHUBA_INJECT_TOKEN, scanFn } from "./nehubaViewer.component"
+import { importNehubaFactory } from "../util"
+import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service"
+import { LoggingModule, LoggingService } from "src/logging"
+import { APPEND_SCRIPT_TOKEN, appendScriptFactory } from "src/util/constants"
+import { IMeshesToLoad, SET_MESHES_TO_LOAD } from "../constants"
+import { ReplaySubject, Subject } from "rxjs"
+import { IColorMap, SET_COLORMAP_OBS, SET_LAYER_VISIBILITY } from "../layerCtrl.service"
+
+describe('> nehubaViewer.component.ts', () => {
+  describe('> #scanFn', () => {
+
+    const curr = {
+      layer: {
+        name: 'foo-bar'
+      },
+      labelIndicies: [1,2,3]
+    }
+
+    describe('> insert OP', () => {
+      describe('> if incoming is empty arr', () => {
+        const acc = []
+        it('> should insert', () => {
+          expect(
+            scanFn(acc, curr)
+          ).toEqual([curr])
+        })
+      })
+
+      describe('> if incoming has other key', () => {
+        it('> should insert', () => {
+          const acc = [{
+            layer: {
+              name: 'hello-world'
+            },
+            labelIndicies: [4,5,6]
+          }]
+          expect(
+            scanFn(acc, curr)
+          ).toEqual([
+            ...acc,
+            curr
+          ])
+        })
+      })
+    })
+
+    describe('> update OP', () => {
+      const acc = [{
+        layer: {
+          name: 'hello-world'
+        },
+        labelIndicies: [4,5,6]
+      }, {
+        layer: {
+          name: 'foo-bar',
+        },
+        labelIndicies: [1]
+      }]
+      it('> should update with same key', () => {
+        expect(
+          scanFn(acc, curr)
+        ).toEqual([{
+          layer: {
+            name: 'hello-world'
+          },
+          labelIndicies: [4,5,6]
+        }, {
+          layer: {
+            name: 'foo-bar',
+          },
+          labelIndicies: [1,2,3]
+        }])
+      })
+    })
+  })
+
+  describe('> NehubaViewerUnit', () => {
+    let provideSetMeshToLoadCtrl = true
+    let provideLayerVisibility = true
+    let provideSetColorObs = true
+    const setMeshToLoadCtl$ = new Subject<IMeshesToLoad>()
+    let setLayerVisibility$: Subject<string[]>
+    let setcolorMap$: Subject<IColorMap>
+    beforeEach(async(() => {
+      TestBed.configureTestingModule({
+        imports: [
+          CommonModule,
+          LoggingModule
+        ],
+        declarations: [
+          NehubaViewerUnit
+        ],
+        providers:[
+          {
+            provide: IMPORT_NEHUBA_INJECT_TOKEN,
+            useFactory: importNehubaFactory,
+            deps: [ APPEND_SCRIPT_TOKEN ]
+          },
+          {
+            provide: APPEND_SCRIPT_TOKEN,
+            useFactory: appendScriptFactory,
+            deps: [ DOCUMENT ]
+          },
+          {
+            provide: SET_MESHES_TO_LOAD,
+            useFactory: () => provideSetMeshToLoadCtrl
+              ? setMeshToLoadCtl$
+              : null
+          },
+          {
+            provide: SET_LAYER_VISIBILITY,
+            useFactory: () => {
+              setLayerVisibility$ = provideLayerVisibility
+                ? new ReplaySubject(1)
+                : null
+              return setLayerVisibility$
+            } 
+          },
+          {
+            provide: SET_COLORMAP_OBS,
+            useFactory: () => {
+              setcolorMap$ = provideSetColorObs
+                ? new ReplaySubject(1)
+                : null
+              return setcolorMap$
+            }
+          },
+          AtlasWorkerService,
+          LoggingService,
+        ]
+      }).compileComponents()
+    }))
+
+    it('> creates component', () => {
+      const fixture = TestBed.createComponent(NehubaViewerUnit)
+      expect(fixture.componentInstance).toBeTruthy()
+    })
+
+    describe('> on create', () => {
+      it('> calls onInit lifecycle param properly', () => {
+        const onInitSpy = jasmine.createSpy('onInit')
+        const fixture = TestBed.createComponent(NehubaViewerUnit)
+        fixture.componentInstance.lifecycle = {
+          onInit: onInitSpy
+        }
+
+        fixture.detectChanges()
+
+        expect(onInitSpy).toHaveBeenCalled()
+      })
+    })
+
+    describe('> loading meshes', () => {
+      describe('> native', () => {
+        beforeAll(() => {
+          provideSetMeshToLoadCtrl = false
+        })
+        it('> on loadMeshes$ emit, calls nehubaViewer.setMeshesToLoad', fakeAsync(() => {
+
+          const fixture = TestBed.createComponent(NehubaViewerUnit)
+          fixture.componentInstance.nehubaViewer = {
+            setMeshesToLoad: jasmine.createSpy('setMeshesToLoad').and.returnValue(null),
+            dispose: () => {}
+          }
+  
+          fixture.detectChanges()
+          fixture.componentInstance['loadMeshes$'].next({
+            layer: {
+              name: 'foo-bar'
+            },
+            labelIndicies: [1,2,3]
+          })
+          tick(1000)
+          expect(fixture.componentInstance.nehubaViewer.setMeshesToLoad).toHaveBeenCalledWith([1,2,3], { name: 'foo-bar' })
+        }))
+      })
+
+      describe('> injecting SET_MESHES_TO_LOAD', () => {
+        beforeAll(() => {
+          provideSetMeshToLoadCtrl = true
+        })
+        it('> navtive loadMeshes method will not trigger loadMesh call',fakeAsync(() => {
+
+          const fixture = TestBed.createComponent(NehubaViewerUnit)
+          fixture.componentInstance.nehubaViewer = {
+            setMeshesToLoad: jasmine.createSpy('setMeshesToLoad').and.returnValue(null),
+            dispose: () => {}
+          }
+  
+          fixture.detectChanges()
+          fixture.componentInstance['loadMeshes$'].next({
+            layer: {
+              name: 'foo-bar'
+            },
+            labelIndicies: [1,2,3]
+          })
+          tick(1000)
+          expect(fixture.componentInstance.nehubaViewer.setMeshesToLoad).not.toHaveBeenCalledWith([1,2,3], { name: 'foo-bar' })
+        }))
+
+        it('> when injected obs emits, will trigger loadMesh call', fakeAsync(() => {
+
+          const fixture = TestBed.createComponent(NehubaViewerUnit)
+          fixture.componentInstance.nehubaViewer = {
+            setMeshesToLoad: jasmine.createSpy('setMeshesToLoad').and.returnValue(null),
+            dispose: () => {}
+          }
+  
+          fixture.detectChanges()
+          setMeshToLoadCtl$.next({
+            labelIndicies: [1,2,3],
+            layer: {
+              name: 'foo-bar'
+            }
+          })
+          tick(1000)
+          expect(fixture.componentInstance.nehubaViewer.setMeshesToLoad).toHaveBeenCalledWith([1,2,3], { name: 'foo-bar' })
+        }))
+      })
+    })
+
+    describe('> layer visibility', () => {
+      let nehubaViewerSpy: any
+      let managedLayersSpy: jasmine.Spy
+      let getLayerByNameSpy: jasmine.Spy
+      let managedLayer = {
+        setVisible: jasmine.createSpy()
+      }
+      let layerManager = {
+        get managedLayers() {
+          return []
+        },
+        getLayerByName(layerName: string){
+
+        }
+      }
+
+      afterEach(() => {
+        managedLayer.setVisible.calls.reset()
+      })
+      beforeEach(() => {
+        managedLayersSpy = spyOnProperty(layerManager, 'managedLayers')
+        managedLayersSpy.and.returnValue([ managedLayer ])
+        getLayerByNameSpy = spyOn(layerManager, 'getLayerByName')
+        getLayerByNameSpy.and.callThrough()
+        
+        nehubaViewerSpy = {
+          ngviewer: {
+            layerManager
+          },
+          dispose: () => {}
+        }
+
+        provideLayerVisibility = true
+      })
+
+      it('> if provided obs does not emit, does not call manage layers', fakeAsync(() => {
+        const fixture = TestBed.createComponent(NehubaViewerUnit)
+        fixture.componentInstance.nehubaViewer = nehubaViewerSpy
+        fixture.detectChanges()
+        tick(320)
+        expect(managedLayersSpy).not.toHaveBeenCalled()
+      }))
+
+      describe('> if provided obs does emit', () => {
+
+        const setup = (emit = []) => {
+          const fixture = TestBed.createComponent(NehubaViewerUnit)
+          setLayerVisibility$.next(emit)
+          fixture.componentInstance.nehubaViewer = nehubaViewerSpy
+          fixture.detectChanges()
+          tick(640)
+        }
+        describe('> emits []', () => {
+          it('> call manage layers', fakeAsync(() => {
+            setup()
+            expect(managedLayersSpy).toHaveBeenCalled()
+          }))
+          it('> layers have visibility set off', fakeAsync(() => {
+            setup()
+            expect(managedLayer.setVisible).toHaveBeenCalledWith(false)
+          }))
+        })
+
+        describe('> emits ["something"]', () => {
+          it('> calls getLayerByname',fakeAsync(() => {
+            setup(['something'])
+            expect(layerManager.getLayerByName).toHaveBeenCalledWith('something')
+          }))
+
+          it('> if returns layer, expects setVisible to be called', fakeAsync(() => {
+            const layer = {
+              setVisible: jasmine.createSpy()
+            }
+            getLayerByNameSpy.and.returnValue(layer)
+            setup(['something'])
+            expect(layer.setVisible).toHaveBeenCalledWith(true)
+          }))
+        })
+      })
+    })
+
+    describe('> colorMap obs', () => {
+
+      let prvSetCMSpy: jasmine.Spy
+      const setup = () => {
+
+        const fixture = TestBed.createComponent(NehubaViewerUnit)
+        /**
+         * set nehubaViewer, since some methods check viewer is loaded
+         */
+         fixture.componentInstance.nehubaViewer = {
+           ngviewer: {}
+         }
+        fixture.detectChanges()
+        prvSetCMSpy = spyOn<any>(fixture.componentInstance, 'setColorMap').and.callFake(() => {})
+      }
+
+      beforeEach(() => {
+        provideSetColorObs = true
+      })
+      it('> if obs does not emit, does not call setcolormap', fakeAsync(() => {
+        setup()
+        tick(320)
+        expect(prvSetCMSpy).not.toHaveBeenCalled()
+      }))
+
+      describe('> if obs does emit', () => {
+        it('> setcolormap gets called', fakeAsync(() => {
+          setup()
+          setcolorMap$.next({
+            'foo-bar': {
+              1: { red: 100, green: 100, blue: 100 },
+              2: { red: 200, green: 200, blue: 200 },
+            },
+            'hello-world': {
+              1: { red: 10, green: 10, blue: 10 },
+              2: { red: 20, green: 20, blue: 20 },
+            }
+          })
+          tick(320)
+          expect(prvSetCMSpy).toHaveBeenCalled()
+        }))
+
+        it('> call arg is as expected', fakeAsync(() => {
+          setup()
+          setcolorMap$.next({
+            'foo-bar': {
+              1: { red: 100, green: 100, blue: 100 },
+              2: { red: 200, green: 200, blue: 200 },
+            },
+            'hello-world': {
+              1: { red: 10, green: 10, blue: 10 },
+              2: { red: 20, green: 20, blue: 20 },
+            }
+          })
+          tick(320)
+          const map = new Map([
+            ['foo-bar', new Map([
+              ['1', { red: 100, green: 100, blue: 100 }],
+              ['2', { red: 200, green: 200, blue: 200 }],
+            ])],
+            ['hello-world', new Map([
+              ['1', { red: 10, green: 10, blue: 10 }],
+              ['2', { red: 20, green: 20, blue: 20 }],
+            ])]
+          ])
+
+          expect(prvSetCMSpy).toHaveBeenCalledWith(map)
+        }))
+      })
+    })
+
+    describe('> # setColorMap', () => {
+      let nehubaViewerSpy: any
+      beforeEach(() => {
+        nehubaViewerSpy = {
+          batchAddAndUpdateSegmentColors: jasmine.createSpy(),
+          dispose(){
+
+          }
+        }
+      })
+      it('> calls nehubaViewer.batchAddAndUpdateSegmentColors', () => {
+        const fixture = TestBed.createComponent(NehubaViewerUnit)
+        fixture.componentInstance.nehubaViewer = nehubaViewerSpy
+        fixture.detectChanges()
+
+        const mainMap = new Map<string, Map<number, { red: number, green: number, blue: number }>>()
+        const fooBarMap = new Map()
+        fooBarMap.set(1, {red: 100, green: 100, blue: 100})
+        fooBarMap.set(2, {red: 200, green: 200, blue: 200})
+        mainMap.set('foo-bar', fooBarMap)
+
+        const helloWorldMap = new Map()
+        helloWorldMap.set(1, {red: 10, green: 10, blue: 10})
+        helloWorldMap.set(2, {red: 20, green: 20, blue: 20})
+        mainMap.set('hello-world', helloWorldMap)
+
+        fixture.componentInstance['setColorMap'](mainMap)
+
+        expect(
+          nehubaViewerSpy.batchAddAndUpdateSegmentColors
+        ).toHaveBeenCalledTimes(2)
+
+        expect(nehubaViewerSpy.batchAddAndUpdateSegmentColors).toHaveBeenCalledWith(
+          fooBarMap,
+          { name: 'foo-bar' }
+        )
+
+        expect(nehubaViewerSpy.batchAddAndUpdateSegmentColors).toHaveBeenCalledWith(
+          helloWorldMap,
+          { name: 'hello-world' }
+        )
+      })
+    })
+
+  })
+})
diff --git a/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts
similarity index 78%
rename from src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts
rename to src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts
index d1edab5e0f9fa80aa6347906b43c5d4ccceef803..3dfad4e558d68a7ca22f97c78d6c6985b8dc8157 100644
--- a/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts
+++ b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts
@@ -1,17 +1,18 @@
-import { Component, ElementRef, EventEmitter, OnDestroy, OnInit, Output, Inject } from "@angular/core";
-import { fromEvent, Subscription, ReplaySubject, BehaviorSubject, Observable, race, timer } from 'rxjs'
-import { debounceTime, filter, map, scan, startWith, mapTo, switchMap, take, skip } from "rxjs/operators";
+import { Component, ElementRef, EventEmitter, OnDestroy, OnInit, Output, Inject, Optional } from "@angular/core";
+import { fromEvent, Subscription, ReplaySubject, BehaviorSubject, Observable, race, timer, Subject } from 'rxjs'
+import { debounceTime, filter, map, scan, startWith, mapTo, switchMap, take, skip, tap } from "rxjs/operators";
 import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service";
 import { StateInterface as ViewerConfiguration } from "src/services/state/viewerConfig.store";
-import { getNgIdLabelIndexFromId } from "src/services/stateStore.service";
-
 import { LoggingService } from "src/logging";
-import { getExportNehuba, getViewer, setNehubaViewer } from "src/util/fn";
+import { bufferUntil, getExportNehuba, getViewer, setNehubaViewer, switchMapWaitFor } from "src/util/fn";
+import { NEHUBA_INSTANCE_INJTKN, scanSliceViewRenderFn } from "../util";
+import { deserialiseParcRegionId } from 'common/util'
+import { IMeshesToLoad, SET_MESHES_TO_LOAD } from "../constants";
+import { IColorMap, SET_COLORMAP_OBS, SET_LAYER_VISIBILITY } from "../layerCtrl.service";
 
 import '!!file-loader?context=third_party&name=main.bundle.js!export-nehuba/dist/min/main.bundle.js'
 import '!!file-loader?context=third_party&name=chunk_worker.bundle.js!export-nehuba/dist/min/chunk_worker.bundle.js'
-import { scanSliceViewRenderFn } from "../util";
-import { intToRgb as intToColour } from 'common/util'
+import { INgLayerCtrl, NG_LAYER_CONTROL, SET_SEGMENT_VISIBILITY, TNgLayerCtrl } from "../layerCtrl.service/layerCtrl.util";
 
 const NG_LANDMARK_LAYER_NAME = 'spatial landmark layer'
 const NG_USER_LANDMARK_LAYER_NAME = 'user landmark layer'
@@ -38,17 +39,18 @@ export interface INehubaLifecycleHook{
   onInit?: () => void
 }
 
-const scanFn: (acc: LayerLabelIndex[], curr: LayerLabelIndex) => LayerLabelIndex[] = (acc: LayerLabelIndex[], curr: LayerLabelIndex) => {
-  const { layer } = curr
-  const { name } = layer
-  const foundIndex = acc.findIndex(({ layer }) => layer.name === name)
-  if (foundIndex < 0) { return acc.concat(curr) } else { return acc.map((item, idx) => idx === foundIndex
-    ? {
-      ...item,
-      labelIndicies: [...new Set([...item.labelIndicies, ...curr.labelIndicies])],
-    }
-    : item)
+export const scanFn = (acc: LayerLabelIndex[], curr: LayerLabelIndex) => {
+  const found = acc.find(layerLabelIndex => {
+    return layerLabelIndex.layer.name === curr.layer.name
+  })
+  if (!found) {
+    return [ ...acc, curr ]
   }
+  return acc.map(layerLabelIndex => {
+    return layerLabelIndex.layer.name === curr.layer.name
+      ? curr
+      : layerLabelIndex
+  })
 }
 
 /**
@@ -66,12 +68,6 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
   private sliceviewLoading$: Observable<boolean>
 
   public overrideShowLayers: string[] = []
-  get showLayersName() {
-    return [
-      ...this.overrideShowLayers,
-      ...Array.from(this.multiNgIdsLabelIndexMap.keys())
-    ]
-  }
 
   public lifecycle: INehubaLifecycleHook
 
@@ -81,14 +77,13 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
   public mousePosInReal$ = new BehaviorSubject(null)
 
   private exportNehuba: any
-  private viewer: any
 
   private subscriptions: Subscription[] = []
 
   @Output() public nehubaReady: EventEmitter<null> = new EventEmitter()
   @Output() public layersChanged: EventEmitter<null> = new EventEmitter()
   private layersChangedHandler: any
-  @Output() public debouncedViewerPositionChange: EventEmitter<any> = new EventEmitter()
+  @Output() public viewerPositionChange: EventEmitter<any> = new EventEmitter()
   @Output() public mouseoverSegmentEmitter:
     EventEmitter<{
       segmentId: number | null
@@ -119,7 +114,6 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
       : [1.5e9, 1.5e9, 1.5e9]
   }
 
-  public _s1$: any = null
   public _s2$: any = null
   public _s3$: any = null
   public _s4$: any = null
@@ -127,10 +121,8 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
   public _s6$: any = null
   public _s7$: any = null
   public _s8$: any = null
-  public _s9$: any = null
 
   public _s$: any[] = [
-    this._s1$,
     this._s2$,
     this._s3$,
     this._s4$,
@@ -138,7 +130,6 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
     this._s6$,
     this._s7$,
     this._s8$,
-    this._s9$,
   ]
 
   public ondestroySubscriptions: Subscription[] = []
@@ -150,13 +141,25 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
 
   public nehubaLoaded: boolean = false
 
+  public landmarksLoaded: boolean = false
+
   constructor(
     public elementRef: ElementRef,
     private workerService: AtlasWorkerService,
     private log: LoggingService,
-    @Inject(IMPORT_NEHUBA_INJECT_TOKEN) getImportNehubaPr: () => Promise<any>
+    @Inject(IMPORT_NEHUBA_INJECT_TOKEN) getImportNehubaPr: () => Promise<any>,
+    @Optional() @Inject(NEHUBA_INSTANCE_INJTKN) private nehubaViewer$: Subject<NehubaViewerUnit>,
+    @Optional() @Inject(SET_MESHES_TO_LOAD) private injSetMeshesToLoad$: Observable<IMeshesToLoad>,
+    @Optional() @Inject(SET_COLORMAP_OBS) private setColormap$: Observable<IColorMap>,
+    @Optional() @Inject(SET_LAYER_VISIBILITY) private layerVis$: Observable<string[]>,
+    @Optional() @Inject(SET_SEGMENT_VISIBILITY) private segVis$: Observable<string[]>,
+    @Optional() @Inject(NG_LAYER_CONTROL) private layerCtrl$: Observable<TNgLayerCtrl<keyof INgLayerCtrl>>,
   ) {
 
+    if (this.nehubaViewer$) {
+      this.nehubaViewer$.next(this)
+    }
+
     getImportNehubaPr()
       .then(() => {
         this.nehubaLoaded = true
@@ -179,6 +182,9 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
       })
       .catch(e => this.errorEmitter.emit(e))
 
+    /**
+     * TODO move to layerCtrl.service
+     */
     this.ondestroySubscriptions.push(
       fromEvent(this.workerService.worker, 'message').pipe(
         filter((message: any) => {
@@ -216,6 +222,9 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
       }),
     )
 
+    /**
+     * TODO move to layerCtrl.service
+     */
     this.ondestroySubscriptions.push(
       fromEvent(this.workerService.worker, 'message').pipe(
         filter((message: any) => {
@@ -245,6 +254,7 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
         debounceTime(100),
         map(e => e.data.url),
       ).subscribe(url => {
+        this.landmarksLoaded = !!url
         this.removeuserLandmarks()
 
         /**
@@ -254,13 +264,7 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
           /**
            * remove transparency from meshes in current layer(s)
            */
-          for (const layerKey of this.multiNgIdsLabelIndexMap.keys()) {
-            const layer = this.nehubaViewer.ngviewer.layerManager.getLayerByName(layerKey)
-            if (layer) {
-              layer.layer.displayState.objectAlpha.restoreState(1.0)
-            }
-          }
-  
+          this.setMeshTransparency(false)
           return
         }
         const _ = {}
@@ -274,18 +278,116 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
         /**
          * adding transparency to meshes in current layer(s)
          */
-        for (const layerKey of this.multiNgIdsLabelIndexMap.keys()) {
-          const layer = this.nehubaViewer.ngviewer.layerManager.getLayerByName(layerKey)
-          if (layer) {
-            layer.layer.displayState.objectAlpha.restoreState(0.2)
-          }
-        }
+        this.setMeshTransparency(true)
       }),
     )
-  }
+  
+    if (this.setColormap$) {
+      this.ondestroySubscriptions.push(
+        this.setColormap$.pipe(
+          switchMap(switchMapWaitFor({
+            condition: () => !!(this.nehubaViewer?.ngviewer)
+          })),
+          debounceTime(160),
+        ).subscribe(v => {
+          const map = new Map()
+          for (const key in v) {
+            const m = new Map()
+            map.set(key, m)
+            for (const lblIdx in v[key]) {
+              m.set(lblIdx, v[key][lblIdx])
+            }
+          }
+          this.setColorMap(map)
+        })
+      )
+    } else {
+      this.log.error(`SET_COLORMAP_OBS not provided`)
+    }
 
-  private _baseUrlToParcellationIdMap: Map<string, string> = new Map()
-  private _baseUrls: string[] = []
+    if (this.layerVis$) {
+      this.ondestroySubscriptions.push(
+        this.layerVis$.pipe(
+          switchMap(switchMapWaitFor({ condition: () => !!(this.nehubaViewer?.ngviewer) })),
+          debounceTime(160),
+        ).subscribe((layerNames: string[]) => {
+          /**
+           * debounce 160ms to set layer invisible etc
+           * on switch from freesurfer -> volumetric viewer, race con results in managed layer not necessarily setting layer visible correctly
+           */
+          const managedLayers = this.nehubaViewer.ngviewer.layerManager.managedLayers
+          managedLayers.forEach(layer => layer.setVisible(false))
+          
+          for (const layerName of layerNames) {
+            const layer = this.nehubaViewer.ngviewer.layerManager.getLayerByName(layerName)
+            if (layer) {
+              layer.setVisible(true)
+            } else {
+              this.log.log('layer unavailable', layerName)
+            }
+          }
+        })
+      )
+    } else {
+      this.log.error(`SET_LAYER_VISIBILITY not provided`)
+    }
+
+    if (this.segVis$) {
+      this.ondestroySubscriptions.push(
+        this.segVis$.pipe().subscribe(val => {
+          // null === hide all seg
+          if (val === null) {
+            this.hideAllSeg()
+            return
+          }
+          // empty array === show all seg
+          if (val.length === 0) {
+            this.showAllSeg()
+            return
+          }
+          // show limited seg
+          this.showSegs(val)
+        })
+      )
+    } else {
+      this.log.error(`SET_SEGMENT_VISIBILITY not provided`)
+    }
+
+    if (this.layerCtrl$) {
+      this.ondestroySubscriptions.push(
+        this.layerCtrl$.pipe(
+          bufferUntil(({
+            condition: () => !!this.nehubaViewer?.ngviewer
+          }))
+        ).subscribe(messages => {
+          for (const message of messages) {
+            if (message.type === 'add') {
+              const p = message as TNgLayerCtrl<'add'>
+              this.loadLayer(p.payload)
+            }
+            if (message.type === 'remove') {
+              const p = message as TNgLayerCtrl<'remove'>
+              for (const name of p.payload.names){
+                this.removeLayer({ name })
+              }
+            }
+            if (message.type === 'update') {
+              const p = message as TNgLayerCtrl<'update'>
+              this.updateLayer(p.payload)
+            }
+            if (message.type === 'setLayerTransparency') {
+              const p = message as TNgLayerCtrl<'setLayerTransparency'>
+              for (const key in p.payload) {
+                this.setLayerTransparency(key, p.payload[key])
+              }
+            }
+          }
+        })
+      )
+    } else {
+      this.log.error(`NG_LAYER_CONTROL not provided`)
+    }
+  }
 
   public numMeshesToBeLoaded: number = 0
 
@@ -307,25 +409,6 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
     this._templateId = id
   }
 
-  /** compatible with multiple parcellation id selection */
-  private _ngIds: string[] = []
-  get ngIds() {
-    return this._ngIds
-  }
-
-  set ngIds(val: string[]) {
-    this.createNehubaPromise
-      .then(() => {
-        this._ngIds.forEach(id => {
-          const oldlayer = this.nehubaViewer.ngviewer.layerManager.getLayerByName(id)
-          if (oldlayer) {oldlayer.setVisible(false) } else { this.log.warn('could not find old layer', id) }
-        })
-        this._ngIds = val
-        this.loadNewParcellation()
-        this.showAllSeg()
-      })
-  }
-
   public spatialLandmarkSelectionChanged(labels: number[]) {
     const getCondition = (label: number) => `if(label > ${label - 0.1} && label < ${label + 0.1} ){${FRAGMENT_EMIT_RED}}`
     const newShader = `void main(){ ${labels.map(getCondition).join('else ')}else {${FRAGMENT_EMIT_WHITE}} }`
@@ -345,6 +428,8 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
     }
   }
 
+  // TODO move region management to another service
+
   public multiNgIdsLabelIndexMap: Map<string, Map<number, any>> = new Map()
 
   public navPosReal: [number, number, number] = [0, 0, 0]
@@ -365,12 +450,6 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
   }
 
   private loadMeshes$: ReplaySubject<{labelIndicies: number[], layer: { name: string }}> = new ReplaySubject()
-  private loadMeshes(labelIndicies: number[], { name }) {
-    this.loadMeshes$.next({
-      labelIndicies,
-      layer: { name },
-    })
-  }
 
   public mouseOverSegment: number | null
   public mouseOverLayer: {name: string, url: string}| null
@@ -393,13 +472,6 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
      * Hide all layers except the base layer (template)
      * Then show the layers referenced in multiNgIdLabelIndexMap
      */
-    const managedLayers = this.nehubaViewer.ngviewer.layerManager.managedLayers
-    managedLayers.slice(1).forEach(layer => layer.setVisible(false))
-
-    for (const layerName of this.showLayersName) {
-      const layer = this.nehubaViewer.ngviewer.layerManager.getLayerByName(layerName)
-      if (layer) { layer.setVisible(true) } else { this.log.log('layer unavailable', layerName) }
-    }
 
     this.redraw()
 
@@ -425,7 +497,7 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
     )
 
     this.subscriptions.push(
-      this.loadMeshes$.pipe(
+      (this.injSetMeshesToLoad$ || this.loadMeshes$).pipe(
         scan(scanFn, []),
         debounceTime(100),
         switchMap(layerLabelIdx => 
@@ -464,6 +536,9 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
   }
 
   public ngOnDestroy() {
+    if (this.nehubaViewer$) {
+      this.nehubaViewer$.next(null)
+    }
     while (this.subscriptions.length > 0) {
       this.subscriptions.pop().unsubscribe()
     }
@@ -636,6 +711,18 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
       })
   }
 
+  public updateLayer(layerObj: INgLayerCtrl['update']) {
+
+    const viewer = this.nehubaViewer.ngviewer
+
+    for (const layerName in layerObj) {
+      const layer = viewer.layerManager.getLayerByName(layerName)
+      if (!layer) continue
+      const { visible } = layerObj[layerName]
+      layer.setVisible(!!visible)
+    }
+  }
+
   public hideAllSeg() {
     if (!this.nehubaViewer) { return }
     Array.from(this.multiNgIdsLabelIndexMap.keys()).forEach(ngId => {
@@ -681,9 +768,13 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
     const reduceFn: (acc: Map<string, number[]>, curr: string) => Map<string, number[]> = (acc, curr) => {
 
       const newMap = new Map(acc)
-      const { ngId, labelIndex } = getNgIdLabelIndexFromId({ labelIndexId: curr })
+      const { ngId, labelIndex } = deserialiseParcRegionId(curr)
       const exist = newMap.get(ngId)
-      if (!exist) { newMap.set(ngId, [Number(labelIndex)]) } else { newMap.set(ngId, [...exist, Number(labelIndex)]) }
+      if (!exist) {
+        newMap.set(ngId, [Number(labelIndex)])
+      } else {
+        newMap.set(ngId, [...exist, Number(labelIndex)])
+      }
       return newMap
     }
 
@@ -758,23 +849,42 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
     this.nehubaViewer.ngviewer.navigationState.pose.rotateRelative(this.vec3([0, 0, 1]), amount / 4.0 * Math.PI / 180.0)
   }
 
-  /**
-   *
-   * @param arrayIdx label indices of the shown segment(s)
-   * @param ngId segmentation layer name
-   */
-  private updateColorMap(arrayIdx: number[], ngId: string) {
-    const set = new Set(arrayIdx)
-    const newColorMap = new Map(
-      Array.from(this.multiNgIdColorMap.get(ngId).entries())
-        .map(v => set.has(v[0]) || set.size === 0 ?
-          v :
-          [v[0], {red: 255, green: 255, blue: 255}]) as any,
-    )
+  public toggleOctantRemoval(flag?: boolean) {
+    const ctrl = this.nehubaViewer?.ngviewer?.showPerspectiveSliceViews
+    if (!ctrl) {
+      this.log.error(`toggleOctantRemoval failed. this.nehubaViewer.ngviewer?.showPerspectiveSliceViews returns falsy`)
+      return
+    }
+    const newVal = typeof flag === 'undefined'
+      ? !ctrl.value
+      : flag
+    ctrl.restoreState(newVal)
 
-    this.nehubaViewer.batchAddAndUpdateSegmentColors(newColorMap, {
-      name: ngId,
-    })
+    if (this.landmarksLoaded) {
+      /**
+       * showPerspectSliceView -> ! meshTransparency
+       */
+      this.setMeshTransparency(!newVal)
+    }
+  }
+
+  private setLayerTransparency(layerName: string, alpha: number) {
+    const layer = this.nehubaViewer.ngviewer.layerManager.getLayerByName(layerName)
+    if (!layer) return
+    layer.layer.displayState.objectAlpha.restoreState(alpha)
+  }
+
+  public setMeshTransparency(flag: boolean){
+
+    /**
+     * remove transparency from meshes in current layer(s)
+     */
+    for (const layerKey of this.multiNgIdsLabelIndexMap.keys()) {
+      const layer = this.nehubaViewer.ngviewer.layerManager.getLayerByName(layerKey)
+      if (layer) {
+        layer.layer.displayState.objectAlpha.restoreState(flag ? 0.2 : 1.0)
+      }
+    }
   }
 
   private newViewerInit() {
@@ -789,6 +899,7 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
 
     if (this.initNav) {
       this.setNavigationState(this.initNav)
+      this.initNav = null
     }
 
     if (this.initRegions && this.initRegions.length > 0) {
@@ -815,7 +926,6 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
 
     // nehubaViewer.navigationState.all emits every time a new layer is added or removed from the viewer
     this._s3$ = this.nehubaViewer.navigationState.all
-      .debounceTime(300)
       .distinctUntilChanged((a, b) => {
         const {
           orientation: o1,
@@ -838,6 +948,7 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
           [0, 1, 2].every(idx => p1[idx] === p2[idx]) &&
           z1 === z2
       })
+      .filter(() => !this.initNav)
       .subscribe(({ orientation, perspectiveOrientation, perspectiveZoom, position, zoom }) => {
         this.viewerState = {
           orientation,
@@ -848,7 +959,7 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
           positionReal : false,
         }
 
-        this.debouncedViewerPositionChange.emit({
+        this.viewerPositionChange.emit({
           orientation : Array.from(orientation),
           perspectiveOrientation : Array.from(perspectiveOrientation),
           perspectiveZoom,
@@ -899,102 +1010,22 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
 
   private loadNewParcellation() {
 
-    /* show correct segmentation layer */
-    this._baseUrls = []
-
-    this.ngIds.map(id => {
-      const newlayer = this.nehubaViewer.ngviewer.layerManager.getLayerByName(id)
-      if (newlayer) {newlayer.setVisible(true) } else { this.log.warn('could not find new layer', id) }
-
-      const regex = /^(\S.*?):\/\/(.*?)$/.exec(newlayer.sourceUrl)
-
-      if (!regex || !regex[2]) {
-        this.log.error('could not parse baseUrl')
-        return
-      }
-      if (regex[1] !== 'precomputed') {
-        this.log.error('sourceUrl is not precomputed')
-        return
-      }
-
-      const baseUrl = regex[2]
-      this._baseUrls.push(baseUrl)
-      this._baseUrlToParcellationIdMap.set(baseUrl, id)
-
-      const indicies = [
-        ...Array.from(this.multiNgIdsLabelIndexMap.get(id).keys()),
-        ...this.auxilaryMeshIndices,
-      ]
-
-      this.loadMeshes(indicies, { name: id })
-    })
-
-    const obj = Array.from(this.multiNgIdsLabelIndexMap.keys()).map(ngId => {
-      return [
-        ngId,
-        new Map(Array.from(
-          [
-            ...this.multiNgIdsLabelIndexMap.get(ngId).entries(),
-            ...this.auxilaryMeshIndices.map(val => {
-              return [val, {}]
-            }),
-          ],
-        ).map((val: [number, any]) => ([val[0], this.getRgb(val[0], val[1].rgb)])) as any),
-      ]
-    }) as Array<[string, Map<number, {red: number, green: number, blue: number}>]>
-
-    const multiNgIdColorMap = new Map(obj)
-
-    /* load colour maps */
-
-    this.setColorMap(multiNgIdColorMap)
-
     this._s$.forEach(_s$ => {
       if (_s$) { _s$.unsubscribe() }
     })
 
-    if (this._s1$) {this._s1$.unsubscribe() }
-    if (this._s9$) {this._s9$.unsubscribe() }
-
-    const arr = Array.from(this.multiNgIdsLabelIndexMap.keys()).map(ngId => {
-      return this.nehubaViewer.getShownSegmentsObservable({
-        name: ngId,
-      }).subscribe(arrayIdx => this.updateColorMap(arrayIdx, ngId))
-    })
-
-    this._s9$ = {
-      unsubscribe: () => {
-        while (arr.length > 0) {
-          arr.pop().unsubscribe()
-        }
-      },
-    }
   }
 
-  public setColorMap(map: Map<string, Map<number, {red: number, green: number, blue: number}>>) {
+  private setColorMap(map: Map<string, Map<number, {red: number, green: number, blue: number}>>) {
     this.multiNgIdColorMap = map
-
-    Array.from(map.entries()).forEach(([ngId, map]) => {
-
+    for (const [ ngId, cMap ] of map.entries()) {
+      const nMap = new Map()
+      for (const [ key, cm ] of cMap.entries()) {
+        nMap.set(Number(key), cm)
+      }
       this.nehubaViewer.batchAddAndUpdateSegmentColors(
-        map,
+        nMap,
         { name : ngId })
-    })
-  }
-
-  private getRgb(labelIndex: number, rgb?: number[]): {red: number, green: number, blue: number} {
-    if (typeof rgb === 'undefined' || rgb === null) {
-      const arr = intToColour(Number(labelIndex))
-      return {
-        red : arr[0],
-        green: arr[1],
-        blue : arr[2],
-      }
-    }
-    return {
-      red : rgb[0],
-      green: rgb[1],
-      blue : rgb[2],
     }
   }
 }
diff --git a/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.style.css b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.style.css
similarity index 100%
rename from src/ui/nehubaContainer/nehubaViewer/nehubaViewer.style.css
rename to src/viewerModule/nehuba/nehubaViewer/nehubaViewer.style.css
diff --git a/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.template.html b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.template.html
similarity index 100%
rename from src/ui/nehubaContainer/nehubaViewer/nehubaViewer.template.html
rename to src/viewerModule/nehuba/nehubaViewer/nehubaViewer.template.html
diff --git a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.spec.ts b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0afa1fda6c3ad59fab421629150d6afa5c5aab81
--- /dev/null
+++ b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.spec.ts
@@ -0,0 +1,173 @@
+import { CommonModule } from "@angular/common"
+import { TestBed } from "@angular/core/testing"
+import { MockStore, provideMockStore } from "@ngrx/store/testing"
+import { Subject } from "rxjs"
+import { ClickInterceptorService } from "src/glue"
+import { PANELS } from "src/services/state/ngViewerState/constants"
+import { ngViewerSelectorOctantRemoval, ngViewerSelectorPanelMode, ngViewerSelectorPanelOrder } from "src/services/state/ngViewerState/selectors"
+import { uiStateMouseOverSegmentsSelector } from "src/services/state/uiState/selectors"
+import { viewerStateSetSelectedRegions } from "src/services/state/viewerState/actions"
+import { viewerStateCustomLandmarkSelector, viewerStateNavigationStateSelector, viewerStateSelectedRegionsSelector } from "src/services/state/viewerState/selectors"
+import { ClickInterceptor, CLICK_INTERCEPTOR_INJECTOR } from "src/util"
+import { NehubaLayerControlService } from "../layerCtrl.service"
+import { NehubaMeshService } from "../mesh.service"
+import { NehubaGlueCmp } from "./nehubaViewerGlue.component"
+
+describe('> nehubaViewerGlue.component.ts', () => {
+  let mockStore: MockStore
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        CommonModule,
+      ],
+      declarations: [
+        NehubaGlueCmp
+      ],
+      providers: [
+        /**
+         * TODO, figureout which dependency is selecting viewerState.parcellationSelected
+         * then remove the inital state
+         */
+        provideMockStore({
+          initialState: {
+            viewerState: {}
+          }
+        }),
+        {
+          provide: CLICK_INTERCEPTOR_INJECTOR,
+          useFactory: (clickIntService: ClickInterceptorService) => {
+            return {
+              deregister: clickIntService.deregister.bind(clickIntService),
+              register: arg => clickIntService.register(arg)
+            } as ClickInterceptor
+          },
+          deps: [
+            ClickInterceptorService
+          ]
+        },{
+          provide: NehubaLayerControlService,
+          useValue: {
+            setColorMap$: new Subject(),
+            visibleLayer$: new Subject(),
+            segmentVis$: new Subject(),
+            ngLayersController$: new Subject(),
+          }
+        }, {
+          provide: NehubaMeshService,
+          useValue: {
+            loadMeshes$: new Subject()
+          }
+        }
+      ]
+    }).overrideComponent(NehubaGlueCmp, {
+      set: {
+        template: '',
+        templateUrl: null
+      }
+    }).compileComponents()
+  })
+
+  beforeEach(() => {
+    mockStore = TestBed.inject(MockStore)
+    mockStore.overrideSelector(ngViewerSelectorPanelMode, PANELS.FOUR_PANEL)
+    mockStore.overrideSelector(ngViewerSelectorPanelOrder, '0123')
+    mockStore.overrideSelector(ngViewerSelectorOctantRemoval, true)
+    mockStore.overrideSelector(viewerStateCustomLandmarkSelector, [])
+    mockStore.overrideSelector(viewerStateSelectedRegionsSelector, [])
+    mockStore.overrideSelector(uiStateMouseOverSegmentsSelector, [])
+    mockStore.overrideSelector(viewerStateNavigationStateSelector, null)
+  })
+
+
+  it('> can be init', () => {
+    const fixture = TestBed.createComponent(NehubaGlueCmp)
+    expect(fixture.componentInstance).toBeTruthy()
+  })
+
+  describe('> selectHoveredRegion', () => {
+    let dispatchSpy: jasmine.Spy
+    let clickIntServ: ClickInterceptorService
+    beforeEach(() => {
+      dispatchSpy = spyOn(mockStore, 'dispatch')
+      clickIntServ = TestBed.inject(ClickInterceptorService)
+    })
+    afterEach(() => {
+      dispatchSpy.calls.reset()
+    })
+
+    describe('> if on hover is empty array', () => {
+      let fallbackSpy: jasmine.Spy
+      beforeEach(() => {
+        fallbackSpy = spyOn(clickIntServ, 'fallback')
+        TestBed.createComponent(NehubaGlueCmp)
+        clickIntServ.callRegFns(null)
+      })
+      it('> dispatch not called', () => {
+        expect(dispatchSpy).not.toHaveBeenCalled()
+      })
+      it('> fallback called', () => {
+        expect(fallbackSpy).toHaveBeenCalled()
+      })
+    })
+
+    describe('> if on hover is non object array', () => {
+      let fallbackSpy: jasmine.Spy
+
+      const testObj0 = {
+        segment: 'hello world'
+      }
+      beforeEach(() => {
+        fallbackSpy = spyOn(clickIntServ, 'fallback')
+        mockStore.overrideSelector(uiStateMouseOverSegmentsSelector, ['hello world', testObj0])
+        TestBed.createComponent(NehubaGlueCmp)
+        clickIntServ.callRegFns(null)
+      })
+      it('> dispatch not called', () => {
+        expect(dispatchSpy).not.toHaveBeenCalled()
+      })
+      it('> fallback called', () => {
+        expect(fallbackSpy).toHaveBeenCalled()
+      })
+    })
+
+    describe('> if on hover array containing at least 1 obj, only dispatch the first obj', () => {
+      let fallbackSpy: jasmine.Spy
+      const testObj0 = {
+        segment: 'hello world'
+      }
+      const testObj1 = {
+        segment: {
+          foo: 'baz'
+        }
+      }
+      const testObj2 = {
+        segment: {
+          hello: 'world'
+        }
+      }
+      beforeEach(() => {
+        fallbackSpy = spyOn(clickIntServ, 'fallback')
+        mockStore.overrideSelector(uiStateMouseOverSegmentsSelector, [testObj0, testObj1, testObj2])
+
+      })
+      afterEach(() => {
+        fallbackSpy.calls.reset()
+      })
+      it('> dispatch called with obj1', () => {
+        TestBed.createComponent(NehubaGlueCmp)
+        clickIntServ.callRegFns(null)
+        const { segment } = testObj1
+        expect(dispatchSpy).toHaveBeenCalledWith(
+          viewerStateSetSelectedRegions({
+            selectRegions: [segment]
+          } as any)
+        )
+      })
+      it('> fallback called (does not intercept)', () => {
+        TestBed.createComponent(NehubaGlueCmp)
+        clickIntServ.callRegFns(null)
+        expect(fallbackSpy).toHaveBeenCalled()
+      })
+    })
+  })
+})
diff --git a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c071875bb5faba2dd631d73585b30e4f8a748fd5
--- /dev/null
+++ b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts
@@ -0,0 +1,756 @@
+import { AfterViewInit, Component, ElementRef, EventEmitter, Inject, Input, OnChanges, OnDestroy, Optional, Output, SimpleChanges, ViewChild } from "@angular/core";
+import { select, Store } from "@ngrx/store";
+import { asyncScheduler, combineLatest, fromEvent, merge, Observable, of, Subject } from "rxjs";
+import { ngViewerActionToggleMax } from "src/services/state/ngViewerState/actions";
+import { ClickInterceptor, CLICK_INTERCEPTOR_INJECTOR } from "src/util";
+import { uiStateMouseOverSegmentsSelector } from "src/services/state/uiState/selectors";
+import { debounceTime, distinctUntilChanged, filter, map, mapTo, scan, shareReplay, startWith, switchMap, switchMapTo, take, tap, throttleTime } from "rxjs/operators";
+import { viewerStateAddUserLandmarks, viewerStateChangeNavigation, viewerStateMouseOverCustomLandmark, viewerStateSelectRegionWithIdDeprecated, viewerStateSetSelectedRegions, viewreStateRemoveUserLandmarks } from "src/services/state/viewerState/actions";
+import { ngViewerSelectorPanelOrder, ngViewerSelectorPanelMode } from "src/services/state/ngViewerState/selectors";
+import { viewerStateCustomLandmarkSelector, viewerStateNavigationStateSelector } from "src/services/state/viewerState/selectors";
+import { serialiseParcellationRegion } from 'common/util'
+import { ARIA_LABELS, IDS, QUICKTOUR_DESC } from 'common/constants'
+import { PANELS } from "src/services/state/ngViewerState/constants";
+import { LoggingService } from "src/logging";
+
+import { getMultiNgIdsRegionsLabelIndexMap, SET_MESHES_TO_LOAD } from "../constants";
+import { EnumViewerEvt, IViewer, TViewerEvent } from "../../viewer.interface";
+import { NehubaViewerUnit } from "../nehubaViewer/nehubaViewer.component";
+import { NehubaViewerContainerDirective, TMouseoverEvent } from "../nehubaViewerInterface/nehubaViewerInterface.directive";
+import { cvtNavigationObjToNehubaConfig, getFourPanel, getHorizontalOneThree, getSinglePanel, getVerticalOneThree, scanSliceViewRenderFn, takeOnePipe } from "../util";
+import { API_SERVICE_SET_VIEWER_HANDLE_TOKEN, TSetViewerHandle } from "src/atlasViewer/atlasViewer.apiService.service";
+import { MouseHoverDirective } from "src/mouseoverModule";
+import { NehubaMeshService } from "../mesh.service";
+import { IQuickTourData } from "src/ui/quickTour/constrants";
+import { NehubaLayerControlService, IColorMap, SET_COLORMAP_OBS, SET_LAYER_VISIBILITY } from "../layerCtrl.service";
+import { switchMapWaitFor } from "src/util/fn";
+import { INavObj } from "../navigation.service";
+import { NG_LAYER_CONTROL, SET_SEGMENT_VISIBILITY } from "../layerCtrl.service/layerCtrl.util";
+
+@Component({
+  selector: 'iav-cmp-viewer-nehuba-glue',
+  templateUrl: './nehubaViewerGlue.template.html',
+  styleUrls: [
+    './nehubaViewerGlue.style.css'
+  ],
+  exportAs: 'iavCmpViewerNehubaGlue',
+  providers: [
+    {
+      provide: SET_MESHES_TO_LOAD,
+      useFactory: (meshService: NehubaMeshService) => meshService.loadMeshes$,
+      deps: [ NehubaMeshService ]
+    },
+    NehubaMeshService,
+    {
+      provide: SET_COLORMAP_OBS,
+      useFactory: (layerCtrl: NehubaLayerControlService) => layerCtrl.setColorMap$,
+      deps: [ NehubaLayerControlService ]
+    },
+    {
+      provide: SET_LAYER_VISIBILITY,
+      useFactory: (layerCtrl: NehubaLayerControlService) => layerCtrl.visibleLayer$,
+      deps: [ NehubaLayerControlService ]
+    },
+    {
+      provide: SET_SEGMENT_VISIBILITY,
+      useFactory: (layerCtrl: NehubaLayerControlService) => layerCtrl.segmentVis$,
+      deps: [ NehubaLayerControlService ]
+    },
+    {
+      provide: NG_LAYER_CONTROL,
+      useFactory: (layerCtrl: NehubaLayerControlService) => layerCtrl.ngLayersController$,
+      deps: [ NehubaLayerControlService ]
+    },
+    NehubaLayerControlService
+  ]
+})
+
+export class NehubaGlueCmp implements IViewer<'nehuba'>, OnChanges, OnDestroy, AfterViewInit {
+
+  public ARIA_LABELS = ARIA_LABELS
+  public IDS = IDS
+
+  @ViewChild(NehubaViewerContainerDirective, { static: true })
+  public nehubaContainerDirective: NehubaViewerContainerDirective
+
+  @ViewChild(MouseHoverDirective, { static: true })
+  private mouseoverDirective: MouseHoverDirective
+
+  public viewerLoaded: boolean = false
+
+  private onhoverSegments = []
+  private onDestroyCb: Function[] = []
+  private viewerUnit: NehubaViewerUnit
+  private multiNgIdsRegionsLabelIndexMap: Map<string, Map<number, any>>
+
+  @Input()
+  public selectedParcellation: any
+
+  @Input()
+  public selectedTemplate: any
+
+  private navigation: any
+
+  private newViewer$ = new Subject()
+
+  public showPerpsectiveScreen$: Observable<string>
+  public sliceViewLoadingMain$: Observable<[boolean, boolean, boolean]>
+  private sliceRenderEvent$: Observable<CustomEvent>
+  public perspectiveViewLoading$: Observable<string|null>
+  public hoveredPanelIndices$: Observable<number>
+  private viewPanelWeakMap = new WeakMap<HTMLElement, number>()
+  public viewPanels: [HTMLElement, HTMLElement, HTMLElement, HTMLElement] = [null, null, null, null]
+  private findPanelIndex = (panel: HTMLElement) => this.viewPanelWeakMap.get(panel)
+  public nanometersToOffsetPixelsFn: Array<(...arg) => any> = []
+
+  public quickTourSliceViewSlide: IQuickTourData = {
+    order: 1,
+    description: QUICKTOUR_DESC.SLICE_VIEW,
+  }
+
+  public quickTour3dViewSlide: IQuickTourData = {
+    order: 2,
+    description: QUICKTOUR_DESC.PERSPECTIVE_VIEW,
+  }
+
+  public quickTourIconsSlide: IQuickTourData = {
+    order: 3,
+    description: QUICKTOUR_DESC.VIEW_ICONS,
+  }
+
+  public customLandmarks$: Observable<any> = this.store$.pipe(
+    select(viewerStateCustomLandmarkSelector),
+    map(lms => lms.map(lm => ({
+      ...lm,
+      geometry: {
+        position: lm.position
+      }
+    }))),
+  )
+
+  public panelOrder$ = this.store$.pipe(
+    select(ngViewerSelectorPanelOrder),
+    distinctUntilChanged(),
+    shareReplay(1),
+  )
+
+  ngOnChanges(sc: SimpleChanges){
+    const {
+      selectedParcellation,
+      selectedTemplate
+    } = sc
+    if (selectedTemplate) {
+      if (selectedTemplate?.currentValue?.['@id'] !== selectedTemplate?.previousValue?.['@id']) {
+
+        if (selectedTemplate?.previousValue) {
+          this.unloadTmpl(selectedTemplate?.previousValue)
+        }
+        if (selectedTemplate?.currentValue?.['@id']) {
+          this.loadTmpl(selectedTemplate.currentValue, selectedParcellation.currentValue)
+        }
+      }
+    }else if (selectedParcellation && selectedParcellation.currentValue !== selectedParcellation.previousValue) {
+      this.loadParc(selectedParcellation.currentValue)
+    }
+  }
+
+  ngAfterViewInit(){
+    this.setQuickTourPos()
+
+    const { 
+      mouseOverSegments,
+      navigationEmitter,
+      mousePosEmitter,
+    } = this.nehubaContainerDirective
+    const sub = combineLatest([
+      mouseOverSegments,
+      navigationEmitter,
+      mousePosEmitter,
+    ]).pipe(
+      throttleTime(16, asyncScheduler, { trailing: true })
+    ).subscribe(([ seg, nav, mouse ]: [ TMouseoverEvent [], INavObj, { real: number[], voxel: number[] } ]) => {
+      this.viewerEvent.emit({
+        type: EnumViewerEvt.VIEWER_CTX,
+        data: {
+          viewerType: 'nehuba',
+          payload: {
+            nav,
+            mouse,
+            nehuba: seg.map(v => {
+              return {
+                layerName: v.layer.name,
+                labelIndices: [ Number(v.segmentId) ]
+              }
+            })
+          }
+        }
+      })
+    })
+    this.onDestroyCb.push(
+      () => sub.unsubscribe()
+    )
+  }
+
+  ngOnDestroy() {
+    while (this.onDestroyCb.length) this.onDestroyCb.pop()()
+  }
+
+  private loadParc(parcellation: any) {
+    /**
+     * parcellaiton may be undefined
+     */
+    if ( !(parcellation && parcellation.regions)) {
+      return
+    }
+
+    this.multiNgIdsRegionsLabelIndexMap = getMultiNgIdsRegionsLabelIndexMap(parcellation)
+
+    this.viewerUnit.multiNgIdsLabelIndexMap = this.multiNgIdsRegionsLabelIndexMap
+    this.viewerUnit.auxilaryMeshIndices = parcellation.auxillaryMeshIndices || []
+
+  }
+
+  private unloadTmpl(tmpl: any) {
+    /**
+     * clear existing container
+     */
+    this.viewerUnit = null
+    this.nehubaContainerDirective.clear()
+
+    /* on selecting of new template, remove additional nglayers */
+    const baseLayerNames = Object.keys(tmpl.nehubaConfig.dataset.initialNgState.layers)
+    this.layerCtrlService.removeNgLayers(baseLayerNames)
+  }
+
+  private async loadTmpl(_template: any, parcellation: any) {
+
+    if (!_template) return
+    /**
+     * recalcuate zoom
+     */
+    const template = (() => {
+
+      const deepCopiedState = JSON.parse(JSON.stringify(_template))
+      const initialNgState = deepCopiedState.nehubaConfig.dataset.initialNgState
+
+      if (!initialNgState || !this.navigation) {
+        return deepCopiedState
+      }
+      const overwritingInitState = this.navigation
+        ? cvtNavigationObjToNehubaConfig(this.navigation, initialNgState)
+        : {}
+
+      deepCopiedState.nehubaConfig.dataset.initialNgState = {
+        ...initialNgState,
+        ...overwritingInitState,
+      }
+      return deepCopiedState
+    })()
+
+    this.nehubaContainerDirective.createNehubaInstance(template)
+    this.viewerUnit = this.nehubaContainerDirective.nehubaViewerInstance
+    this.sliceRenderEvent$.pipe(
+      takeOnePipe()
+    ).subscribe(ev => {
+
+      for (const idx of [0, 1, 2]) {
+        const e = ev[idx] as CustomEvent
+        const el = e.target as HTMLElement
+        this.viewPanelWeakMap.set(el, idx)
+        this.viewPanels[idx] = el
+        this.nanometersToOffsetPixelsFn[idx] = e.detail.nanometersToOffsetPixels
+      }
+    })
+    const foundParcellation = parcellation
+      && template?.parcellations?.find(p => parcellation.name === p.name)
+    this.loadParc(foundParcellation || template.parcellations[0])
+
+    const nehubaConfig = template.nehubaConfig
+    const initialSpec = nehubaConfig.dataset.initialNgState
+    const {layers} = initialSpec
+
+    const dispatchLayers = Object.keys(layers).map(key => {
+      const layer = {
+        name : key,
+        source : layers[key].source,
+        mixability : layers[key].type === 'image'
+          ? 'base'
+          : 'mixable',
+        visible : typeof layers[key].visible === 'undefined'
+          ? true
+          : layers[key].visible,
+        transform : typeof layers[key].transform === 'undefined'
+          ? null
+          : layers[key].transform,
+      }
+      return layer
+    })
+
+    this.layerCtrlService.addNgLayer(dispatchLayers)
+    this.newViewer$.next(true)
+  }
+
+  @Output()
+  public viewerEvent = new EventEmitter<TViewerEvent<'nehuba'>>()
+
+  constructor(
+    private store$: Store<any>,
+    private el: ElementRef,
+    private log: LoggingService,
+    @Optional() @Inject(CLICK_INTERCEPTOR_INJECTOR) clickInterceptor: ClickInterceptor,
+    @Optional() @Inject(API_SERVICE_SET_VIEWER_HANDLE_TOKEN) setViewerHandle: TSetViewerHandle,
+    @Optional() private layerCtrlService: NehubaLayerControlService,
+  ){
+
+    /**
+     * define onclick behaviour
+     */
+    if (clickInterceptor) {
+      const { deregister, register } = clickInterceptor
+      const selOnhoverRegion = this.selectHoveredRegion.bind(this)
+      register(selOnhoverRegion, { last: true })
+      this.onDestroyCb.push(() => deregister(selOnhoverRegion))
+    }
+
+    /**
+     * on layout change
+     */
+    const redrawLayoutSub = combineLatest([
+      this.store$.pipe(
+        select(ngViewerSelectorPanelMode),
+        distinctUntilChanged(),
+        shareReplay(1),
+      ),
+      this.panelOrder$,
+    ]).pipe(
+      switchMap(this.waitForNehuba.bind(this))
+    ).subscribe(([mode, panelOrder]) => {
+      const viewPanels = panelOrder.split('').map(v => Number(v)).map(idx => this.viewPanels[idx]) as [HTMLElement, HTMLElement, HTMLElement, HTMLElement]
+
+      /**
+       * TODO smarter with event stream
+       */
+      if (!viewPanels.every(v => !!v)) {
+        this.log.error(`on relayout, not every view panel is populated. This should not occur!`)
+        return
+      }
+
+      switch (mode) {
+      case PANELS.H_ONE_THREE: {
+        const element = this.removeExistingPanels()
+        const newEl = getHorizontalOneThree(viewPanels)
+        element.appendChild(newEl)
+        break;
+      }
+      case PANELS.V_ONE_THREE: {
+        const element = this.removeExistingPanels()
+        const newEl = getVerticalOneThree(viewPanels)
+        element.appendChild(newEl)
+        break;
+      }
+      case PANELS.FOUR_PANEL: {
+        const element = this.removeExistingPanels()
+        const newEl = getFourPanel(viewPanels)
+        element.appendChild(newEl)
+        break;
+      }
+      case PANELS.SINGLE_PANEL: {
+        const element = this.removeExistingPanels()
+        const newEl = getSinglePanel(viewPanels)
+        element.appendChild(newEl)
+        break;
+      }
+      default:
+      }
+      for (const panel of viewPanels) {
+        (panel as HTMLElement).classList.add('neuroglancer-panel')
+      }
+
+      // TODO needed to redraw?
+      // see https://trello.com/c/oJOnlc6v/60-enlarge-panel-allow-user-rearrange-panel-position
+      // further investigaation required
+      this.nehubaContainerDirective.nehubaViewerInstance.redraw()
+    })
+    this.onDestroyCb.push(() => redrawLayoutSub.unsubscribe())
+
+    /**
+     * on hover segment
+     */
+    const onhovSegSub = this.store$.pipe(
+      select(uiStateMouseOverSegmentsSelector),
+      distinctUntilChanged(),
+    ).subscribe(arr => {
+      const segments = arr.map(({ segment }) => segment).filter(v => !!v)
+      this.onhoverSegments = segments
+    })
+    this.onDestroyCb.push(() => onhovSegSub.unsubscribe())
+
+    const perspectiveRenderEvSub = this.newViewer$.pipe(
+      switchMapTo(fromEvent<CustomEvent>(this.el.nativeElement, 'perpspectiveRenderEvent').pipe(
+        take(1)
+      ))
+    ).subscribe(ev => {
+      const perspPanel = ev.target as HTMLElement
+      this.viewPanels[3] = perspPanel
+      this.viewPanelWeakMap.set(perspPanel, 3)
+    })
+    this.onDestroyCb.push(() => perspectiveRenderEvSub.unsubscribe())
+
+    this.sliceRenderEvent$ = fromEvent<CustomEvent>(this.el.nativeElement, 'sliceRenderEvent')
+    this.sliceViewLoadingMain$ = this.sliceRenderEvent$.pipe(
+      scan(scanSliceViewRenderFn, [null, null, null]),
+      startWith([true, true, true] as [boolean, boolean, boolean]),
+      shareReplay(1),
+    )
+
+    this.perspectiveViewLoading$ = fromEvent(this.el.nativeElement, 'perpspectiveRenderEvent').pipe(
+      filter((event: CustomEvent) => event?.detail?.lastLoadedMeshId ),
+      map(event => {
+
+        /**
+         * TODO dig into event detail to see if the exact mesh loaded
+         */
+        const { meshesLoaded, meshFragmentsLoaded: _meshFragmentsLoaded, lastLoadedMeshId: _lastLoadedMeshId } = (event as any).detail
+        return meshesLoaded >= this.nehubaContainerDirective.nehubaViewerInstance.numMeshesToBeLoaded
+          ? null
+          : 'Loading meshes ...'
+      }),
+      distinctUntilChanged()
+    )
+
+    this.showPerpsectiveScreen$ = this.newViewer$.pipe(
+      switchMapTo(this.sliceRenderEvent$.pipe(
+        scan((acc, curr) => {
+
+          /**
+           * if at any point, all chunks have been loaded, always return loaded state
+           */
+          if (acc.every(v => v === 0)) return [0, 0, 0]
+          const { detail = {}, target } = curr || {}
+          const { missingChunks = -1, missingImageChunks = -1 } = detail
+          const idx = this.findPanelIndex(target as HTMLElement)
+          const returnAcc = [...acc]
+          if (idx >= 0) {
+            returnAcc[idx] = missingChunks + missingImageChunks
+          }
+          return returnAcc
+        }, [-1, -1, -1]),
+        map(arr => {
+          let sum = 0
+          let uncertain = false
+          for (const num of arr) {
+            if (num < 0) {
+              uncertain = true
+            } else {
+              sum += num
+            }
+          }
+          return sum > 0
+            ? `Loading ${sum}${uncertain ? '+' : ''} chunks ...`
+            : null
+        }),
+        distinctUntilChanged(),
+        startWith('Loading ...'),
+        throttleTime(100, asyncScheduler, { leading: true, trailing: true }),
+        shareReplay(1),
+      ))
+    )
+
+    this.hoveredPanelIndices$ = fromEvent(this.el.nativeElement, 'mouseover').pipe(
+      switchMap((ev: MouseEvent) => merge(
+        of(this.findPanelIndex(ev.target as HTMLElement)),
+        fromEvent(this.el.nativeElement, 'mouseout').pipe(
+          mapTo(null),
+        ),
+      )),
+      debounceTime(20),
+      shareReplay(1),
+    )
+
+    const setupViewerApiSub = this.newViewer$.pipe(
+      tap(() => {
+        setViewerHandle && setViewerHandle(null)
+      }),
+      switchMap(this.waitForNehuba.bind(this))
+    ).subscribe(() => {
+      setViewerHandle && setViewerHandle({
+        setNavigationLoc : (coord, realSpace?) => this.nehubaContainerDirective.nehubaViewerInstance.setNavigationState({
+          position : coord,
+          positionReal : typeof realSpace !== 'undefined' ? realSpace : true,
+        }),
+        /* TODO introduce animation */
+        moveToNavigationLoc : (coord, _realSpace?) => {
+          this.store$.dispatch(
+            viewerStateChangeNavigation({
+              navigation: {
+                position: coord,
+                animation: {},
+              }
+            })
+          )
+        },
+        setNavigationOri : (quat) => this.nehubaContainerDirective.nehubaViewerInstance.setNavigationState({
+          orientation : quat,
+        }),
+        /* TODO introduce animation */
+        moveToNavigationOri : (quat) => this.nehubaContainerDirective.nehubaViewerInstance.setNavigationState({
+          orientation : quat,
+        }),
+        showSegment : (_labelIndex) => {
+          /**
+           * TODO reenable with updated select_regions api
+           */
+          this.log.warn(`showSegment is temporarily disabled`)
+
+          // if(!this.selectedRegionIndexSet.has(labelIndex))
+          //   this.store.dispatch({
+          //     type : SELECT_REGIONS,
+          //     selectRegions :  [labelIndex, ...this.selectedRegionIndexSet]
+          //   })
+        },
+        add3DLandmarks : landmarks => {
+          // TODO check uniqueness of ID
+          if (!landmarks.every(l => !!l.id)) {
+            throw new Error('every landmarks needs to be identified with the id field')
+          }
+          if (!landmarks.every(l => !!l.position)) {
+            throw new Error('every landmarks needs to have position defined')
+          }
+          if (!landmarks.every(l => l.position.constructor === Array) || !landmarks.every(l => l.position.every(v => !isNaN(v))) || !landmarks.every(l => l.position.length == 3)) {
+            throw new Error('position needs to be a length 3 tuple of numbers ')
+          }
+
+          this.store$.dispatch(viewerStateAddUserLandmarks({
+            landmarks
+          }))
+        },
+        remove3DLandmarks : landmarkIds => {
+          this.store$.dispatch(viewreStateRemoveUserLandmarks({
+            payload: { landmarkIds }
+          }))
+        },
+        hideSegment : (_labelIndex) => {
+          /**
+           * TODO reenable with updated select_regions api
+           */
+          this.log.warn(`hideSegment is temporarily disabled`)
+
+          // if(this.selectedRegionIndexSet.has(labelIndex)){
+          //   this.store.dispatch({
+          //     type :SELECT_REGIONS,
+          //     selectRegions : [...this.selectedRegionIndexSet].filter(num=>num!==labelIndex)
+          //   })
+          // }
+        },
+        showAllSegments : () => {
+          const selectRegionIds = []
+          this.multiNgIdsRegionsLabelIndexMap.forEach((map, ngId) => {
+            Array.from(map.keys()).forEach(labelIndex => {
+              selectRegionIds.push(serialiseParcellationRegion({ ngId, labelIndex }))
+            })
+          })
+          this.store$.dispatch(viewerStateSelectRegionWithIdDeprecated({
+            selectRegionIds
+          }))
+        },
+        hideAllSegments : () => {
+          this.store$.dispatch(viewerStateSelectRegionWithIdDeprecated({
+            selectRegionIds: []
+          }))
+        },
+        getLayersSegmentColourMap: () => {
+          if (!this.layerCtrlService) {
+            throw new Error(`layerCtrlService not injected. Cannot call getLayersSegmentColourMap`)
+          }
+          const newMainMap = new Map()
+          for (const key in this.layerCtrlService.activeColorMap) {
+            const obj = this.layerCtrlService.activeColorMap[key]
+            const m = new Map()
+            newMainMap.set(key, m)
+            for (const labelIndex in obj) {
+              m.set(Number(labelIndex), obj[labelIndex])
+            }
+          }
+          return newMainMap
+        },
+        applyLayersColourMap: (map) => {
+          if (!this.layerCtrlService) {
+            throw new Error(`layerCtrlService not injected. Cannot call getLayersSegmentColourMap`)
+          }
+          const obj: IColorMap = {}
+          for (const [ key, value ] of map.entries()) {
+            const cmap = obj[key] = {}
+            for (const [ labelIdx, rgb ] of value.entries()) {
+              cmap[Number(labelIdx)] = rgb
+            }
+          }
+          this.layerCtrlService.overwriteColorMap$.next(obj)
+        },
+        /**
+         * TODO go via layerCtrl.service
+         */
+        loadLayer : (layerObj) => this.nehubaContainerDirective.nehubaViewerInstance.loadLayer(layerObj),
+        removeLayer : (condition) => this.nehubaContainerDirective.nehubaViewerInstance.removeLayer(condition),
+        setLayerVisibility : (condition, visible) => this.nehubaContainerDirective.nehubaViewerInstance.setLayerVisibility(condition, visible),
+        mouseEvent : merge(
+          fromEvent(this.el.nativeElement, 'click').pipe(
+            map((ev: MouseEvent) => ({eventName : 'click', event: ev})),
+          ),
+          fromEvent(this.el.nativeElement, 'mousemove').pipe(
+            map((ev: MouseEvent) => ({eventName : 'mousemove', event: ev})),
+          ),
+          /**
+           * neuroglancer prevents propagation, so use capture instead
+           */
+          fromEvent(this.el.nativeElement, 'mousedown', { capture: true }).pipe(
+            map((event: MouseEvent) => {
+              return {
+                eventName: 'mousedown',
+                event
+              }
+            })
+          ),
+          fromEvent(this.el.nativeElement, 'mouseup').pipe(
+            map((ev: MouseEvent) => ({eventName : 'mouseup', event: ev})),
+          ),
+        ) ,
+        mouseOverNehuba : of(null).pipe(
+          tap(() => console.warn('mouseOverNehuba observable is becoming deprecated. use mouseOverNehubaLayers instead.')),
+        ),
+        mouseOverNehubaLayers: this.mouseoverDirective.currentOnHoverObs$.pipe(
+          map(({ segments }) => segments)
+        ),
+        mouseOverNehubaUI: this.mouseoverDirective.currentOnHoverObs$.pipe(
+          map(({ landmark, segments, userLandmark: customLandmark }) => ({ segments, landmark, customLandmark })),
+          shareReplay(1),
+        ),
+        getNgHash : this.nehubaContainerDirective.nehubaViewerInstance.getNgHash,
+      })
+    })
+    this.onDestroyCb.push(() => setupViewerApiSub.unsubscribe())
+
+    // listen to navigation change from store
+    const navSub = this.store$.pipe(
+      select(viewerStateNavigationStateSelector)
+    ).subscribe(nav => this.navigation = nav)
+    this.onDestroyCb.push(() => navSub.unsubscribe())
+  }
+
+  handleViewerLoadedEvent(flag: boolean) {
+    this.viewerEvent.emit({
+      type: EnumViewerEvt.VIEWERLOADED,
+      data: flag
+    })
+    this.viewerLoaded = flag
+  }
+
+  private selectHoveredRegion(_ev: any): boolean{
+    /**
+     * If label indicies are not defined by the ontology, it will be a string in the format of `{ngId}#{labelIndex}`
+     */
+    const trueOnhoverSegments = this.onhoverSegments && this.onhoverSegments.filter(v => typeof v === 'object')
+    if (!trueOnhoverSegments || (trueOnhoverSegments.length === 0)) return true
+    this.store$.dispatch(
+      viewerStateSetSelectedRegions({
+        selectRegions: trueOnhoverSegments.slice(0, 1)
+      })
+    )
+    return true
+  }
+
+  private waitForNehuba = switchMapWaitFor({
+    condition: () => !!(this.nehubaContainerDirective?.isReady())
+  }) 
+
+  public toggleMaximiseMinimise(index: number) {
+    this.store$.dispatch(ngViewerActionToggleMax({
+      payload: { index }
+    }))
+  }
+
+  public zoomNgView(panelIndex: number, factor: number) {
+    const ngviewer = this.nehubaContainerDirective?.nehubaViewerInstance?.nehubaViewer?.ngviewer
+    if (!ngviewer) throw new Error(`ngviewer not defined!`)
+
+    /**
+     * panelIndex < 3 === slice view
+     */
+    if (panelIndex < 3) {
+      /**
+       * factor > 1 === zoom out
+       */
+      ngviewer.navigationState.zoomBy(factor)
+    } else {
+      ngviewer.perspectiveNavigationState.zoomBy(factor)
+    }
+  }
+
+  private removeExistingPanels() {
+    const element = this.nehubaContainerDirective.nehubaViewerInstance.nehubaViewer.ngviewer.layout.container.componentValue.element as HTMLElement
+    while (element.childElementCount > 0) {
+      element.removeChild(element.firstElementChild)
+    }
+    return element
+  }
+
+
+  public returnTruePos(quadrant: number, data: any) {
+    const pos = quadrant > 2
+      ? [0, 0, 0]
+      : this.nanometersToOffsetPixelsFn && this.nanometersToOffsetPixelsFn[quadrant]
+        ? this.nanometersToOffsetPixelsFn[quadrant](data.geometry.position.map(n => n * 1e6))
+        : [0, 0, 0]
+    return pos
+  }
+
+  public getPositionX(quadrant: number, data: any) {
+    return this.returnTruePos(quadrant, data)[0]
+  }
+  public getPositionY(quadrant: number, data: any) {
+    return this.returnTruePos(quadrant, data)[1]
+  }
+  public getPositionZ(quadrant: number, data: any) {
+    return this.returnTruePos(quadrant, data)[2]
+  }
+
+  public handleMouseEnterCustomLandmark(lm) {
+    this.store$.dispatch(
+      viewerStateMouseOverCustomLandmark({
+        payload: { userLandmark: lm }
+      })
+    )
+  }
+
+  public handleMouseLeaveCustomLandmark(_lm) {
+    this.store$.dispatch(
+      viewerStateMouseOverCustomLandmark({
+        payload: { userLandmark: null }
+      })
+    )
+  }
+
+  public quickTourOverwritingPos = {
+    'dialog': {
+      left: '0px',
+      top: '0px',
+    },
+    'arrow': {
+      left: '0px',
+      top: '0px',
+    }
+  }
+
+  setQuickTourPos(){
+    const { innerWidth, innerHeight } = window
+    this.quickTourOverwritingPos = {
+      'dialog': {
+        left: `${innerWidth / 2}px`,
+        top: `${innerHeight / 2}px`,
+      },
+      'arrow': {
+        left: `${innerWidth / 2 - 48}px`,
+        top: `${innerHeight / 2 - 48}px`,
+      }
+    }
+  }
+}
diff --git a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.style.css b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..a03d05dd199a30e712eb7062ee253473ae894552
--- /dev/null
+++ b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.style.css
@@ -0,0 +1,31 @@
+
+.opacity-crossfade
+{
+  transition: opacity 170ms ease-in-out,
+    transform 250ms ease-in-out;
+}
+
+.opacity-crossfade
+{
+
+  opacity: 0.0;
+  pointer-events: none;
+}
+
+.opacity-crossfade.onHover,
+.opacity-crossfade:hover,
+:host-context([ismobile="true"]) .opacity-crossfade.always-show-touchdevice
+{
+  opacity: 1.0 !important;
+  pointer-events: all !important;
+}
+
+.screen-overlay
+{
+  background-color: rgba(255, 255, 255, 0.7);
+}
+
+:host-context([darktheme="true"]) .screen-overlay
+{
+  background-color: rgba(0, 0, 0, 0.7);
+}
diff --git a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.template.html b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..d79c49a59f4708a74e9ee9520fcd4038b6be0980
--- /dev/null
+++ b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.template.html
@@ -0,0 +1,192 @@
+<div class="d-block w-100 h-100"
+  (touchmove)="$event.preventDefault()"
+  iav-viewer-touch-interface
+  [iav-viewer-touch-interface-v-panels]="viewPanels"
+  [iav-viewer-touch-interface-vp-to-data]="iavContainer?.viewportToDatas"
+  [iav-viewer-touch-interface-ngviewer]="iavContainer?.nehubaViewerInstance?.nehubaViewer?.ngviewer"
+  [iav-viewer-touch-interface-nehuba-config]="selectedTemplate?.nehubaConfig">
+
+  <div class="d-block"
+    iav-nehuba-viewer-container
+    #iavContainer="iavNehubaViewerContainer"
+    iav-mouse-hover
+    (iavNehubaViewerContainerViewerLoading)="handleViewerLoadedEvent($event)">
+
+  </div>
+</div>
+
+<current-layout *ngIf="viewerLoaded" class="position-absolute w-100 h-100 d-block pe-none top-0 left-0">
+  <div class="w-100 h-100 position-relative" cell-i
+    iav-window-resize
+    [iav-window-resize-time]="64"
+    (iav-window-resize-event)="setQuickTourPos()"
+    quick-tour
+    [quick-tour-description]="quickTourSliceViewSlide.description"
+    [quick-tour-order]="quickTourSliceViewSlide.order"
+    [quick-tour-overwrite-arrow]="sliceViewArrow"
+    [quick-tour-overwrite-position]="quickTourOverwritingPos">
+    <ng-content *ngTemplateOutlet="ngPanelOverlayTmpl; context: { panelIndex: panelOrder$ | async | getNthElement : 0 | parseAsNumber }"></ng-content>
+  </div>
+  <div class="w-100 h-100 position-relative" cell-ii>
+    <ng-content *ngTemplateOutlet="ngPanelOverlayTmpl; context: { panelIndex: panelOrder$ | async | getNthElement : 1 | parseAsNumber }"></ng-content>
+  </div>
+  <div class="w-100 h-100 position-relative" cell-iii>
+    <ng-content *ngTemplateOutlet="ngPanelOverlayTmpl; context: { panelIndex: panelOrder$ | async | getNthElement : 2 | parseAsNumber }"></ng-content>
+  </div>
+  <div class="w-100 h-100 position-relative" cell-iv
+    quick-tour
+    [quick-tour-description]="quickTour3dViewSlide.description"
+    [quick-tour-order]="quickTour3dViewSlide.order">
+    <ng-content *ngTemplateOutlet="ngPanelOverlayTmpl; context: { panelIndex: panelOrder$ | async | getNthElement : 3 | parseAsNumber }"></ng-content>
+  </div>
+</current-layout>
+
+<!-- slice view overlay tmpl -->
+<ng-template #ngPanelOverlayTmpl let-panelIndex="panelIndex">
+
+  <!-- perspective view tmpl -->
+  <ng-template #overlayPerspectiveTmpl>
+    <layout-floating-container landmarkContainer>
+
+      <div class="d-flex flex-column justify-content-center align-items-center w-100 h-100 position-absolute opacity-crossfade screen-overlay pe-none"
+        [ngClass]="{onHover: !!(showPerpsectiveScreen$ | async)}"
+        [attr.id]="IDS.MESH_LOADING_STATUS"
+        role="status">
+
+        <spinner-cmp *ngIf="showPerpsectiveScreen$ | async">
+        </spinner-cmp>
+
+        <mat-list>
+          <mat-list-item>
+            {{ showPerpsectiveScreen$ | async }}
+          </mat-list-item>
+        </mat-list>
+      </div>
+
+      <!-- mesh loading is still weird -->
+      <!-- if the precomputed server does not have the necessary fragment file, then the numberws will not collate -->
+      <div *ngIf="false && (perspectiveViewLoading$ | async)" class="loadingIndicator">
+        <spinner-cmp></spinner-cmp>
+
+        <div *ngIf="false" perspectiveLoadingText>
+          {{ perspectiveViewLoading$ | async }}
+        </div>
+      </div>
+    </layout-floating-container>
+  </ng-template>
+
+  <iav-layout-fourcorners class="w-100 h-100 d-block">
+    <layout-floating-container *ngIf="panelIndex < 3; else overlayPerspectiveTmpl"
+      class="overflow-hidden"
+      iavLayoutFourCornersContent>
+      <!-- TODO add landmarks here -->
+
+
+      <!-- customLandmarks -->
+      <!-- only show landmarks in slice views -->
+      <landmark-2d-flat-cmp *ngFor="let lm of (customLandmarks$ | async | filterByProperty : 'showInSliceView')"
+        (mouseenter)="handleMouseEnterCustomLandmark(lm)"
+        (mouseleave)="handleMouseLeaveCustomLandmark(lm)"
+        [color]="lm.color || [255, 255, 255]"
+        [positionX]="getPositionX(panelIndex, lm)"
+        [positionY]="getPositionY(panelIndex, lm)"
+        [positionZ]="getPositionZ(panelIndex, lm)">
+
+      </landmark-2d-flat-cmp>
+    </layout-floating-container>
+
+    <!-- panel controller -->
+    <div iavLayoutFourCornersBottomRight class="position-relative">
+
+      <ng-container *ngTemplateOutlet="panelCtrlTmpl; context: {
+        panelIndex: panelIndex,
+        visible: (hoveredPanelIndices$ | async) === panelIndex
+      }">
+      </ng-container>
+
+      <div *ngIf="(sliceViewLoadingMain$ | async)[panelIndex]"
+        class="position-absolute bottom-0 right-0">
+        <spinner-cmp></spinner-cmp>
+      </div>
+    </div>
+  </iav-layout-fourcorners>
+
+</ng-template>
+
+<!-- panel control template -->
+<ng-template
+  #panelCtrlTmpl
+  let-panelIndex="panelIndex"
+  let-visible="visible">
+
+  <div class="opacity-crossfade always-show-touchdevice pe-all overlay-btn-container"
+    [ngClass]="{ onHover: visible }"
+    [attr.data-viewer-controller-visible]="visible"
+    [attr.data-viewer-controller-index]="panelIndex">
+
+    <div class="position-absolute w-100 h-100 pe-none"
+      *ngIf="panelIndex === 1"
+      quick-tour
+      [quick-tour-description]="quickTourIconsSlide.description"
+      [quick-tour-order]="quickTourIconsSlide.order">
+    </div>
+
+    <!-- perspective specific control -->
+    <ng-container *ngIf="panelIndex === 3">
+
+      <button mat-icon-button color="primary"
+        [matMenuTriggerFor]="viewerCtrlMenu">
+        <i class="fas fa-cog"></i>
+      </button>
+
+    </ng-container>
+
+    <!-- factor < 1.0 === zoom in -->
+    <button mat-icon-button color="primary"
+      (click)="zoomNgView(panelIndex, 0.9)"
+      [attr.aria-label]="ARIA_LABELS.ZOOM_IN">
+      <i class="fas fa-search-plus"></i>
+    </button>
+
+    <!-- factor > 1.0 === zoom out -->
+    <button mat-icon-button color="primary"
+      (click)="zoomNgView(panelIndex, 1.1)"
+      [attr.aria-label]="ARIA_LABELS.ZOOM_OUT">
+      <i class="fas fa-search-minus"></i>
+    </button>
+
+    <maximise-panel-button
+      (click)="toggleMaximiseMinimise(panelIndex)"
+      [touch-side-class]="panelIndex">
+    </maximise-panel-button>
+  </div>
+
+</ng-template>
+
+<!-- viewer ctrl -->
+<mat-menu #viewerCtrlMenu>
+  <!-- NB must not lazy load. key listener needs to work even when component is not yet rendered -->
+  <!-- stop propagation is needed, or else click will result in dismiss of menu -->
+  <viewer-ctrl-component class="d-block m-2 ml-3 mr-3"
+    #viewerCtrlCmp="viewerCtrlCmp"
+    (click)="$event.stopPropagation()">
+  </viewer-ctrl-component>
+</mat-menu>
+
+<ng-template #sliceViewArrow>
+  <svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
+    <path id="quarter_circle" d="M22.6151 96.5C22.6151 96.5 18.5 84.1266 18.5 76.5C18.5001 62 18.1151 59.5 22.6151 47C27.115 34.5 39.3315 27.7229 47.5 25C56.5 22 63 22.5 72.5 24C83.1615 25.6834 83.5 26 91 29" />
+    <g id="arrow_top_left">
+      <path id="arrow_stem" d="M37 40C35.5882 38.5882 17.6863 20.6863 12 15" />
+      <path id="arrow_head" d="M6 24C6.38926 21.7912 6.68496 18.3286 6.71205 16.0803C6.73751 13.9665 6.632 13.6135 6.52632 11.5C6.46368 10.2469 6.52632 11.5 6 8C11 10 9.71916 9.74382 11 9.99999C13.5 10.5 13.743 10.7451 17 11C20 11.2348 21.1276 11 22 11"  stroke-linecap="round" stroke-linejoin="round"/>
+    </g>
+    <g id="arrow_left">
+      <path id="arrow_stem_2" d="M29.4229 78.5771C27.1573 78.5771 18.3177 78.5771 9.19238 78.5771" />
+      <path id="arrow_head_2" d="M13.3137 89.6274C12.0271 87.7903 9.78778 85.1328 8.2171 83.5238C6.74048 82.0112 6.41626 81.8362 4.84703 80.4164C3.91668 79.5747 4.84703 80.4164 2 78.3137C6.94975 76.1924 5.86291 76.9169 6.94974 76.1924C9.07106 74.7782 9.41624 74.7797 11.8995 72.6569C14.1868 70.7016 14.8181 69.7382 15.435 69.1213"  stroke-linecap="round" stroke-linejoin="round"/>
+    </g>
+    <g id="arrow_top">
+      <path id="arrow_stem_3" d="M77.0057 32.0057C77.0057 30.3124 77.0057 16.2488 77.0057 9.42862" />
+      <path id="arrow_head_3" d="M68.4342 11.1429C69.9189 10.1032 72.0665 8.29351 73.3667 7.02421C74.5891 5.83091 74.7305 5.5689 75.8779 4.30076C76.5581 3.54892 75.8779 4.30076 77.5771 2C79.2914 6.00002 78.7059 5.12172 79.2915 6.00002C80.4343 7.71431 80.4331 7.99326 82.1486 10C83.7287 11.8485 84.5072 12.3587 85.0058 12.8572"  stroke-linecap="round" stroke-linejoin="round"/>
+    </g>
+  </svg>
+</ng-template>
diff --git a/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.spec.ts b/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..42c350441b7aba57d1a3d15eece54a238e25b2a7
--- /dev/null
+++ b/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.spec.ts
@@ -0,0 +1,298 @@
+import { Component } from "@angular/core"
+import { TestBed, async, ComponentFixture, fakeAsync, tick } from "@angular/core/testing"
+import { By } from "@angular/platform-browser"
+import { BrowserDynamicTestingModule } from "@angular/platform-browser-dynamic/testing"
+import { MockStore, provideMockStore } from "@ngrx/store/testing"
+import { ngViewerSelectorOctantRemoval } from "src/services/state/ngViewerState/selectors"
+import { NehubaViewerUnit } from "../nehubaViewer/nehubaViewer.component"
+import { NehubaViewerContainerDirective } from "./nehubaViewerInterface.directive"
+import { viewerStateSelectorNavigation, viewerStateStandAloneVolumes } from "src/services/state/viewerState/selectors";
+import { Subject } from "rxjs"
+import { ngViewerActionNehubaReady } from "src/services/state/ngViewerState/actions"
+import { viewerStateMouseOverCustomLandmarkInPerspectiveView } from "src/services/state/viewerState/actions"
+import { selectViewerConfigAnimationFlag } from "src/services/state/viewerConfig/selectors"
+
+describe('> nehubaViewerInterface.directive.ts', () => {
+  describe('> NehubaViewerContainerDirective', () => {
+
+    @Component({
+      template: ''
+    })
+    class DummyCmp{}
+
+    beforeEach(async(() => {
+      TestBed.configureTestingModule({
+        imports: [
+          
+        ],
+        declarations: [
+          NehubaViewerContainerDirective,
+          DummyCmp,
+          NehubaViewerUnit,
+        ],
+        providers: [
+          provideMockStore({ initialState: {} })
+        ]
+      }).overrideModule(BrowserDynamicTestingModule,{
+        set: {
+          entryComponents: [
+            NehubaViewerUnit
+          ]
+        }
+      }).overrideComponent(DummyCmp, {
+        set: {
+          template: `
+          <div iav-nehuba-viewer-container>
+          </div>
+          `
+        }
+      }).compileComponents()
+
+    }))
+
+    beforeEach(() => {
+      const mockStore = TestBed.inject(MockStore)
+      mockStore.overrideSelector(ngViewerSelectorOctantRemoval, true)
+      mockStore.overrideSelector(viewerStateStandAloneVolumes, [])
+      mockStore.overrideSelector(viewerStateSelectorNavigation, null)
+      mockStore.overrideSelector(selectViewerConfigAnimationFlag, false)
+    })
+
+    it('> can be inited', () => {
+
+      const fixture = TestBed.createComponent(DummyCmp)
+      fixture.detectChanges()
+      const directive = fixture.debugElement.query(
+        By.directive(NehubaViewerContainerDirective)
+      )
+
+      expect(directive).toBeTruthy()
+    })
+
+    describe('> on createNehubaInstance', () => {
+      let fixture: ComponentFixture<DummyCmp>
+      let directiveInstance: NehubaViewerContainerDirective
+      let nehubaViewerInstanceSpy: jasmine.Spy
+      let elClearSpy: jasmine.Spy
+      let elCreateComponentSpy: jasmine.Spy
+      const spyNehubaViewerInstance = {
+        config: null,
+        lifecycle: null,
+        templateId: null,
+        errorEmitter: new Subject(),
+        viewerPositionChange: new Subject(),
+        layersChanged: new Subject(),
+        nehubaReady: new Subject(),
+        mouseoverSegmentEmitter: new Subject(),
+        mouseoverLandmarkEmitter: new Subject(),
+        mouseoverUserlandmarkEmitter: new Subject(),
+        elementRef: {
+          nativeElement: {}
+        },
+        toggleOctantRemoval: jasmine.createSpy()
+      }
+      const spyComRef = {
+        destroy: jasmine.createSpy('destroy')
+      }
+
+      beforeEach(() => {
+        fixture = TestBed.createComponent(DummyCmp)
+        const directive = fixture.debugElement.query(
+          By.directive(NehubaViewerContainerDirective)
+        )
+        
+        directiveInstance = directive.injector.get(NehubaViewerContainerDirective)
+        
+        nehubaViewerInstanceSpy = spyOnProperty(directiveInstance, 'nehubaViewerInstance').and.returnValue(spyNehubaViewerInstance)
+        elClearSpy = spyOn(directiveInstance['el'], 'clear')
+        // casting return value to any is not perfect, but since only 2 methods and 1 property is used, it's a quick way 
+        // rather than allow component to be created
+        elCreateComponentSpy = spyOn(directiveInstance['el'], 'createComponent').and.returnValue(spyComRef as any)
+      })
+
+      describe('> on createNehubaInstance called', () => {
+        const template = {}
+        const lifecycle = {}
+        it('> method el.clear gets called before el.createComponent', () => {
+          directiveInstance.createNehubaInstance(template, lifecycle)
+          expect(elClearSpy).toHaveBeenCalledBefore(elCreateComponentSpy)
+        })
+
+        it('> if viewerConfig has gpuLimit, gpuMemoryLimit will be in initialNgSTate', () => {
+          template['nehubaConfig'] = {
+            dataset: {
+              initialNgState: {}
+            }
+          }
+          directiveInstance['viewerConfig'] = {
+            gpuLimit: 5e8
+          }
+          directiveInstance.createNehubaInstance(template, lifecycle)
+          expect(
+            directiveInstance.nehubaViewerInstance?.config?.dataset?.initialNgState?.gpuMemoryLimit
+          ).toEqual(5e8)
+          expect(
+            directiveInstance.nehubaViewerInstance?.config?.dataset?.initialNgState?.gpuLimit
+          ).toBeFalsy()
+        })
+      })
+    
+      describe('> on clear called', () => {
+        it('> dispatches nehubaReady: false action', () => {
+          const mockStore = TestBed.inject(MockStore)
+          const mockStoreDispatchSpy = spyOn(mockStore, 'dispatch')
+          directiveInstance.clear()
+          expect(
+            mockStoreDispatchSpy
+          ).toHaveBeenCalledWith(
+            ngViewerActionNehubaReady({
+              nehubaReady: false
+            })
+          )
+        })
+
+        it('> iavNehubaViewerContainerViewerLoading emits false', () => {
+          const emitSpy = spyOn(directiveInstance.iavNehubaViewerContainerViewerLoading, 'emit')
+          directiveInstance.clear()
+          expect(emitSpy).toHaveBeenCalledWith(false)
+        })
+
+        it('> elClear called', () => {
+          directiveInstance.clear()
+          expect(elClearSpy).toHaveBeenCalled()
+        })
+      })
+    
+    })
+  
+    describe('> subscription of nehuba instance', () => {
+      describe('> mouseoverUserlandmarkEmitter', () => {
+        let spyNehubaViewerInstance: any
+        let dispatchSpy: jasmine.Spy
+        let directiveInstance: NehubaViewerContainerDirective
+        const template = {}
+        const lifecycle = {}
+        beforeEach(() => {
+
+          spyNehubaViewerInstance = {
+            config: null,
+            lifecycle: null,
+            templateId: null,
+            errorEmitter: new Subject(),
+            viewerPositionChange: new Subject(),
+            layersChanged: new Subject(),
+            nehubaReady: new Subject(),
+            mouseoverSegmentEmitter: new Subject(),
+            mouseoverLandmarkEmitter: new Subject(),
+            mouseoverUserlandmarkEmitter: new Subject(),
+            elementRef: {
+              nativeElement: {}
+            }
+          }
+          const mockStore = TestBed.inject(MockStore)
+          dispatchSpy = spyOn(mockStore, 'dispatch')
+
+          const fixture = TestBed.createComponent(DummyCmp)
+          const directive = fixture.debugElement.query(
+            By.directive(NehubaViewerContainerDirective)
+          )
+          
+          const spyComRef = {
+            destroy: jasmine.createSpy('destroy')
+          }
+          directiveInstance = directive.injector.get(NehubaViewerContainerDirective)
+          spyOnProperty(directiveInstance, 'nehubaViewerInstance').and.returnValue(spyNehubaViewerInstance)
+          spyOn(directiveInstance['el'], 'clear').and.callFake(() => {})
+          spyOn(directiveInstance, 'clear').and.callFake(() => {})
+          // casting return value to any is not perfect, but since only 2 methods and 1 property is used, it's a quick way 
+          // rather than allow component to be created
+          spyOn(directiveInstance['el'], 'createComponent').and.returnValue(spyComRef as any)
+
+        })
+
+        afterEach(() => {
+          dispatchSpy.calls.reset()
+        })
+        it('> single null emits null', fakeAsync(() => {
+
+          directiveInstance.createNehubaInstance(template, lifecycle)
+          spyNehubaViewerInstance.mouseoverUserlandmarkEmitter.next(null)
+
+          tick(200)
+          expect(dispatchSpy).toHaveBeenCalledWith(
+            viewerStateMouseOverCustomLandmarkInPerspectiveView({
+              payload: { label: null }
+            })
+          )
+        }))
+
+        it('> single value emits value', fakeAsync(() => {
+
+          directiveInstance.createNehubaInstance(template, lifecycle)
+          spyNehubaViewerInstance.mouseoverUserlandmarkEmitter.next("24")
+
+          tick(200)
+          expect(dispatchSpy).toHaveBeenCalledWith(
+            viewerStateMouseOverCustomLandmarkInPerspectiveView({
+              payload: { label: "24" }
+            })
+          )
+        }))
+
+        describe('> double value in 140ms emits last value', () => {
+
+          it('> null - 24 emits 24', fakeAsync(() => {
+
+            directiveInstance.createNehubaInstance(template, lifecycle)
+            spyNehubaViewerInstance.mouseoverUserlandmarkEmitter.next(null)
+            spyNehubaViewerInstance.mouseoverUserlandmarkEmitter.next("24")
+  
+            tick(200)
+            expect(dispatchSpy).toHaveBeenCalledWith(
+              viewerStateMouseOverCustomLandmarkInPerspectiveView({
+                payload: { label: "24" }
+              })
+            )
+          }))
+          it('> 24 - null emits null', fakeAsync(() => {
+
+            directiveInstance.createNehubaInstance(template, lifecycle)
+            spyNehubaViewerInstance.mouseoverUserlandmarkEmitter.next("24")
+            spyNehubaViewerInstance.mouseoverUserlandmarkEmitter.next(null)
+  
+            tick(200)
+            expect(dispatchSpy).toHaveBeenCalledWith(
+              viewerStateMouseOverCustomLandmarkInPerspectiveView({
+                payload: { label: null }
+              })
+            )
+          }))
+        })
+      
+        it('> single value outside 140 ms emits separately', fakeAsync(() => {
+
+          directiveInstance.createNehubaInstance(template, lifecycle)
+          spyNehubaViewerInstance.mouseoverUserlandmarkEmitter.next(null)
+          tick(200)
+          spyNehubaViewerInstance.mouseoverUserlandmarkEmitter.next("24")
+
+          tick(200)
+          expect(
+            dispatchSpy.calls.allArgs()
+          ).toEqual([
+            [
+              viewerStateMouseOverCustomLandmarkInPerspectiveView({
+                payload: { label: null }
+              })
+            ],
+            [
+              viewerStateMouseOverCustomLandmarkInPerspectiveView({
+                payload: { label: "24" }
+              })
+            ]
+          ])
+        }))
+      })
+    })
+  })
+})
diff --git a/src/ui/nehubaContainer/nehubaViewerInterface/nehubaViewerInterface.directive.ts b/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.ts
similarity index 53%
rename from src/ui/nehubaContainer/nehubaViewerInterface/nehubaViewerInterface.directive.ts
rename to src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.ts
index f4a1973ae240f4235f441159dc0892456c381f2f..1b6fb599e73e9808098fe45872860672a26b5742 100644
--- a/src/ui/nehubaContainer/nehubaViewerInterface/nehubaViewerInterface.directive.ts
+++ b/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.ts
@@ -1,19 +1,18 @@
 import { Directive, ViewContainerRef, ComponentFactoryResolver, ComponentFactory, ComponentRef, OnInit, OnDestroy, Output, EventEmitter, Optional } from "@angular/core";
 import { NehubaViewerUnit, INehubaLifecycleHook } from "../nehubaViewer/nehubaViewer.component";
 import { Store, select } from "@ngrx/store";
-import { IavRootStoreInterface } from "src/services/stateStore.service";
-import { Subscription, Observable, fromEvent } from "rxjs";
-import { distinctUntilChanged, filter, debounceTime, shareReplay, scan, map, throttleTime, switchMapTo } from "rxjs/operators";
-import { StateInterface as ViewerConfigStateInterface } from "src/services/state/viewerConfig.store";
-import { getNavigationStateFromConfig, takeOnePipe } from "../util";
-import { NEHUBA_LAYER_CHANGED, CHANGE_NAVIGATION } from "src/services/state/viewerState.store";
-import { timedValues } from "src/util/generator";
-import { MOUSE_OVER_SEGMENTS, MOUSE_OVER_LANDMARK } from "src/services/state/uiState.store";
+import { Subscription, Observable, fromEvent, asyncScheduler, combineLatest } from "rxjs";
+import { distinctUntilChanged, filter, debounceTime, scan, map, throttleTime, switchMapTo } from "rxjs/operators";
+import { takeOnePipe } from "../util";
 import { ngViewerActionNehubaReady } from "src/services/state/ngViewerState/actions";
-import { viewerStateMouseOverCustomLandmarkInPerspectiveView } from "src/services/state/viewerState/actions";
-import { viewerStateStandAloneVolumes, viewerStateSelectorNavigation } from "src/services/state/viewerState/selectors";
+import { viewerStateMouseOverCustomLandmarkInPerspectiveView, viewerStateNehubaLayerchanged } from "src/services/state/viewerState/actions";
+import { viewerStateStandAloneVolumes } from "src/services/state/viewerState/selectors";
 import { ngViewerSelectorOctantRemoval } from "src/services/state/ngViewerState/selectors";
 import { LoggingService } from "src/logging";
+import { uiActionMouseoverLandmark, uiActionMouseoverSegments } from "src/services/state/uiState/actions";
+import { IViewerConfigState } from "src/services/state/viewerConfig.store.helper";
+import { arrayOfPrimitiveEqual } from 'src/util/fn'
+import { INavObj, NehubaNavigationService } from "../navigation.service";
 
 const defaultNehubaConfig = {
   "configName": "",
@@ -45,30 +44,6 @@ const defaultNehubaConfig = {
     "initialNgState": {
       "showDefaultAnnotations": false,
       "layers": {},
-      // "navigation": {
-      //   "pose": {
-      //     "position": {
-      //       "voxelSize": [
-      //         21166.666015625,
-      //         20000,
-      //         21166.666015625
-      //       ],
-      //       "voxelCoordinates": [
-      //         -21.8844051361084,
-      //         16.288618087768555,
-      //         28.418994903564453
-      //       ]
-      //     }
-      //   },
-      //   "zoomFactor": 350000
-      // },
-      // "perspectiveOrientation": [
-      //   0.3140767216682434,
-      //   -0.7418519854545593,
-      //   0.4988985061645508,
-      //   -0.3195493221282959
-      // ],
-      // "perspectiveZoom": 1922235.5293810747
     }
   },
   "layout": {
@@ -88,27 +63,12 @@ const defaultNehubaConfig = {
         1,
         1
       ],
-      // "removePerspectiveSlicesBackground": {
-      //   "color": [
-      //     1,
-      //     1,
-      //     1,
-      //     1
-      //   ],
-      //   "mode": "=="
-      // },
       "perspectiveBackground": [
         1,
         1,
         1,
         1
       ],
-      // "fixedZoomPerspectiveSlices": {
-      //   "sliceViewportWidth": 300,
-      //   "sliceViewportHeight": 300,
-      //   "sliceZoom": 563818.3562426177,
-      //   "sliceViewportSizeMultiplier": 2
-      // },
       "mesh": {
         "backFaceColor": [
           1,
@@ -119,30 +79,8 @@ const defaultNehubaConfig = {
         "removeBasedOnNavigation": true,
         "flipRemovedOctant": true
       },
-      // "centerToOrigin": true,
-      // "drawSubstrates": {
-      //   "color": [
-      //     0,
-      //     0,
-      //     0.5,
-      //     0.15
-      //   ]
-      // },
-      // "drawZoomLevels": {
-      //   "cutOff": 200000,
-      //   "color": [
-      //     0.5,
-      //     0,
-      //     0,
-      //     0.15
-      //   ]
-      // },
       "hideImages": false,
       "waitForMesh": false,
-      // "restrictZoomLevel": {
-      //   "minZoom": 1200000,
-      //   "maxZoom": 3500000
-      // }
     }
   }
 }
@@ -173,6 +111,14 @@ interface IProcessedVolume{
   }
 }
 
+export type TMouseoverEvent = {
+  layer: {
+    name: string
+  }
+  segment: any | string
+  segmentId: string
+}
+
 const processStandaloneVolume: (url: string) => Promise<IProcessedVolume> = async (url: string) => {
   const protocol = determineProtocol(url)
   if (protocol === 'nifti'){
@@ -257,13 +203,22 @@ const accumulatorFn: (
 
 @Directive({
   selector: '[iav-nehuba-viewer-container]',
-  exportAs: 'iavNehubaViewerContainer'
+  exportAs: 'iavNehubaViewerContainer',
+  providers: [ NehubaNavigationService ]
 })
-
 export class NehubaViewerContainerDirective implements OnInit, OnDestroy{
 
   public viewportToDatas: [any, any, any] = [null, null, null]
 
+  @Output('iav-nehuba-viewer-container-mouseover')
+  public mouseOverSegments = new EventEmitter<TMouseoverEvent[]>()
+
+  @Output('iav-nehuba-viewer-container-navigation')
+  public navigationEmitter = new EventEmitter<INavObj>()
+
+  @Output('iav-nehuba-viewer-container-mouse-pos')
+  public mousePosEmitter = new EventEmitter<{ voxel: number[], real: number[] }>()
+
   @Output()
   public iavNehubaViewerContainerViewerLoading: EventEmitter<boolean> = new EventEmitter()
   
@@ -272,7 +227,8 @@ export class NehubaViewerContainerDirective implements OnInit, OnDestroy{
   constructor(
     private el: ViewContainerRef,
     private cfr: ComponentFactoryResolver,
-    private store$: Store<IavRootStoreInterface>,
+    private store$: Store<any>,
+    private navService: NehubaNavigationService,
     @Optional() private log: LoggingService,
   ){
     this.nehubaViewerFactory = this.cfr.resolveComponentFactory(NehubaViewerUnit)
@@ -285,11 +241,6 @@ export class NehubaViewerContainerDirective implements OnInit, OnDestroy{
        */
       distinctUntilChanged(),
     )
-    
-    this.navigationChanges$ = this.store$.pipe(
-      select(viewerStateSelectorNavigation),
-      filter(v => !!v),
-    )
 
     this.nehubaViewerPerspectiveOctantRemoval$ = this.store$.pipe(
       select(ngViewerSelectorOctantRemoval),
@@ -297,13 +248,9 @@ export class NehubaViewerContainerDirective implements OnInit, OnDestroy{
   }
 
   private nehubaViewerPerspectiveOctantRemoval$: Observable<boolean>
-  private navigationChanges$: Observable<any>
 
-  private viewerPerformanceConfig$: Observable<ViewerConfigStateInterface>
-  private viewerConfig: Partial<ViewerConfigStateInterface> = {}
-
-  public oldNavigation: any = {}
-  private storedNav: any
+  private viewerPerformanceConfig$: Observable<IViewerConfigState>
+  private viewerConfig: Partial<IViewerConfigState> = {}
 
   private nehubaViewerSubscriptions: Subscription[] = []
   private subscriptions: Subscription[] = []
@@ -321,7 +268,7 @@ export class NehubaViewerContainerDirective implements OnInit, OnDestroy{
       this.store$.pipe(
         select(viewerStateStandAloneVolumes),
         filter(v => v && Array.isArray(v) && v.length > 0),
-        distinctUntilChanged()
+        distinctUntilChanged(arrayOfPrimitiveEqual)
       ).subscribe(async volumes => {
         const copiedNehubaConfig = JSON.parse(JSON.stringify(defaultNehubaConfig))
 
@@ -349,18 +296,9 @@ export class NehubaViewerContainerDirective implements OnInit, OnDestroy{
           this.nehubaViewerInstance.applyPerformanceConfig(config)
         }
       }),
-
-
-      this.navigationChanges$.subscribe(ev => {
-        if (this.nehubaViewerInstance) {
-          this.handleDispatchedNavigationChange(ev)
-        } else {
-          this.storedNav = {
-            ...ev,
-            positionReal: true
-          }
-        }
-      }),
+      this.navService.viewerNav$.subscribe(v => {
+        this.navigationEmitter.emit(v)
+      })
     )
   }
 
@@ -371,11 +309,11 @@ export class NehubaViewerContainerDirective implements OnInit, OnDestroy{
   }
 
   public toggleOctantRemoval(flag: boolean){
-    const showPerspectiveSliceViews = this.nehubaViewerInstance?.nehubaViewer?.ngviewer?.showPerspectiveSliceViews
-    if (showPerspectiveSliceViews) showPerspectiveSliceViews.restoreState(flag)
-    else {
-      this.log && this.log.warn(`showPerspectiveSliceViews not defined`)
+    if (!this.nehubaViewerInstance) {
+      this.log.error(`this.nehubaViewerInstance is not yet available`)
+      return
     }
+    this.nehubaViewerInstance.toggleOctantRemoval(flag)
   }
 
   createNehubaInstance(template: any, lifeCycle: INehubaLifecycleHook = {}){
@@ -383,12 +321,14 @@ export class NehubaViewerContainerDirective implements OnInit, OnDestroy{
     this.iavNehubaViewerContainerViewerLoading.emit(true)
     this.cr = this.el.createComponent(this.nehubaViewerFactory)
 
-    if (this.storedNav) {
-      this.nehubaViewerInstance.initNav = this.storedNav
-      this.storedNav = null
+    if (this.navService.storeNav) {
+      this.nehubaViewerInstance.initNav = {
+        ...this.navService.storeNav,
+        positionReal: true
+      }
     }
 
-    const { nehubaConfig } = template
+    const { nehubaConfig, name } = template
 
     /**
      * apply viewer config such as gpu limit
@@ -398,12 +338,10 @@ export class NehubaViewerContainerDirective implements OnInit, OnDestroy{
     this.nehubaViewerInstance.config = nehubaConfig
     this.nehubaViewerInstance.lifecycle = lifeCycle
 
-    this.oldNavigation = getNavigationStateFromConfig(nehubaConfig)
-    this.handleEmittedNavigationChange(this.oldNavigation)
-
     if (gpuLimit) {
       const initialNgState = nehubaConfig && nehubaConfig.dataset && nehubaConfig.dataset.initialNgState
-      initialNgState.gpuLimit = gpuLimit
+      // the correct key is gpuMemoryLimit
+      initialNgState.gpuMemoryLimit = gpuLimit
     }
 
     /* TODO replace with id from KG */
@@ -414,14 +352,10 @@ export class NehubaViewerContainerDirective implements OnInit, OnDestroy{
         console.log(e)
       }),
 
-      this.nehubaViewerInstance.debouncedViewerPositionChange.subscribe(val => {
-        this.handleEmittedNavigationChange(val)
-      }),
-
       this.nehubaViewerInstance.layersChanged.subscribe(() => {
-        this.store$.dispatch({
-          type: NEHUBA_LAYER_CHANGED
-        })
+        this.store$.dispatch(
+          viewerStateNehubaLayerchanged()
+        )
       }),
 
       this.nehubaViewerInstance.nehubaReady.subscribe(() => {
@@ -437,32 +371,21 @@ export class NehubaViewerContainerDirective implements OnInit, OnDestroy{
 
       this.nehubaViewerInstance.mouseoverSegmentEmitter.pipe(
         scan(accumulatorFn, new Map()),
-        map(map => Array.from(map.entries()).filter(([_ngId, { segmentId }]) => segmentId)),
-      ).subscribe(arrOfArr => {
-        this.store$.dispatch({
-          type: MOUSE_OVER_SEGMENTS,
-          segments: arrOfArr.map( ([ngId, {segment, segmentId}]) => {
-            return {
-              layer: {
-                name: ngId,
-              },
-              segment: segment || `${ngId}#${segmentId}`,
-            }
-          } ),
-        })
-      }),
+        map((map: Map<string, any>) => Array.from(map.entries()).filter(([_ngId, { segmentId }]) => segmentId)),
+      ).subscribe(val => this.handleMouseoverSegments(val)),
 
       this.nehubaViewerInstance.mouseoverLandmarkEmitter.pipe(
         distinctUntilChanged()
       ).subscribe(label => {
-        this.store$.dispatch({
-          type : MOUSE_OVER_LANDMARK,
-          landmark : label,
-        })
+        this.store$.dispatch(
+          uiActionMouseoverLandmark({
+            landmark: label
+          })
+        )
       }),
 
       this.nehubaViewerInstance.mouseoverUserlandmarkEmitter.pipe(
-        throttleTime(160),
+        throttleTime(160, asyncScheduler, {trailing: true}),
       ).subscribe(label => {
         this.store$.dispatch(
           viewerStateMouseOverCustomLandmarkInPerspectiveView({
@@ -477,6 +400,16 @@ export class NehubaViewerContainerDirective implements OnInit, OnDestroy{
       ).subscribe((events: CustomEvent[]) => {
         [0, 1, 2].forEach(idx => this.viewportToDatas[idx] = events[idx].detail.viewportToData)
       }),
+
+      combineLatest([
+        this.nehubaViewerInstance.mousePosInVoxel$,
+        this.nehubaViewerInstance.mousePosInReal$
+      ]).subscribe(([ voxel, real ]) => {
+        this.mousePosEmitter.emit({
+          voxel,
+          real
+        })
+      })
     )
   }
 
@@ -501,100 +434,25 @@ export class NehubaViewerContainerDirective implements OnInit, OnDestroy{
     return this.cr && this.cr.instance
   }
 
-  /* because the navigation can be changed from two sources,
-    either dynamically (e.g. navigation panel in the UI or plugins etc)
-    or actively (via user interaction with the viewer)
-    or lastly, set on init
-
-  This handler function is meant to handle anytime viewer's navigation changes from either sources */
-  public handleEmittedNavigationChange(navigation) {
-
-    /* If the navigation is changed dynamically, this.oldnavigation is set prior to the propagation of the navigation state to the viewer.
-      As the viewer updates the dynamically changed navigation, it will emit the navigation state.
-      The emitted navigation state should be identical to this.oldnavigation */
-
-    const navigationChangedActively: boolean = Object.keys(this.oldNavigation).length === 0 || !Object.keys(this.oldNavigation).every(key => {
-      return this.oldNavigation[key].constructor === Number || this.oldNavigation[key].constructor === Boolean ?
-        this.oldNavigation[key] === navigation[key] :
-        this.oldNavigation[key].every((_, idx) => this.oldNavigation[key][idx] === navigation[key][idx])
-    })
-
-    /* if navigation is changed dynamically (ie not actively), the state would have been propagated to the store already. Hence return */
-    if ( !navigationChangedActively ) { return }
-
-    /* navigation changed actively (by user interaction with the viewer)
-      probagate the changes to the store */
-
-    this.store$.dispatch({
-      type : CHANGE_NAVIGATION,
-      navigation,
-    })
+  isReady() {
+    return !!(this.cr?.instance?.nehubaViewer?.ngviewer)
   }
 
-
-  public handleDispatchedNavigationChange(navigation) {
-
-    /* extract the animation object */
-    const { animation, ..._navigation } = navigation
-
-    /**
-     * remove keys that are falsy
-     */
-    Object.keys(_navigation).forEach(key => (!_navigation[key]) && delete _navigation[key])
-
-    const { animation: globalAnimationFlag } = this.viewerConfig
-    if ( globalAnimationFlag && animation ) {
-      /* animated */
-
-      const gen = timedValues()
-      const dest = Object.assign({}, _navigation)
-      /* this.oldNavigation is old */
-      const delta = Object.assign({}, ...Object.keys(dest).filter(key => key !== 'positionReal').map(key => {
-        const returnObj = {}
-        returnObj[key] = typeof dest[key] === 'number' ?
-          dest[key] - this.oldNavigation[key] :
-          typeof dest[key] === 'object' ?
-            dest[key].map((val, idx) => val - this.oldNavigation[key][idx]) :
-            true
-        return returnObj
-      }))
-
-      const animate = () => {
-        const next = gen.next()
-        const d =  next.value
-
-        this.nehubaViewerInstance.setNavigationState(
-          Object.assign({}, ...Object.keys(dest).filter(k => k !== 'positionReal').map(key => {
-            const returnObj = {}
-            returnObj[key] = typeof dest[key] === 'number' ?
-              dest[key] - ( delta[key] * ( 1 - d ) ) :
-              dest[key].map((val, idx) => val - ( delta[key][idx] * ( 1 - d ) ) )
-            return returnObj
-          }), {
-            positionReal : true,
-          }),
-        )
-
-        if ( !next.done ) {
-          requestAnimationFrame(() => animate())
-        } else {
-
-          /* set this.oldnavigation to represent the state of the store */
-          /* animation done, set this.oldNavigation */
-          this.oldNavigation = Object.assign({}, this.oldNavigation, dest)
-        }
+  handleMouseoverSegments(arrOfArr: [string, any][]) {
+    const payload = arrOfArr.map( ([ngId, {segment, segmentId}]) => {
+      return {
+        layer: {
+          name: ngId,
+        },
+        segment: segment || `${ngId}#${segmentId}`,
+        segmentId
       }
-      requestAnimationFrame(() => animate())
-    } else {
-      /* not animated */
-
-      /* set this.oldnavigation to represent the state of the store */
-      /* since the emitted change of navigation state is debounced, we can safely set this.oldNavigation to the destination */
-      this.oldNavigation = Object.assign({}, this.oldNavigation, _navigation)
-
-      this.nehubaViewerInstance.setNavigationState(Object.assign({}, _navigation, {
-        positionReal : true,
-      }))
-    }
+    })
+    this.mouseOverSegments.emit(payload)
+    this.store$.dispatch(
+      uiActionMouseoverSegments({
+        segments: payload
+      })
+    )
   }
 }
diff --git a/src/ui/nehubaContainer/nehubaViewerInterface/nehubaViewerTouch.directive.ts b/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerTouch.directive.ts
similarity index 98%
rename from src/ui/nehubaContainer/nehubaViewerInterface/nehubaViewerTouch.directive.ts
rename to src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerTouch.directive.ts
index 99841bfc0ec082fc6ae98907320141e045b0f2e1..28eeda243621049da1342169ef19d70e721eb459 100644
--- a/src/ui/nehubaContainer/nehubaViewerInterface/nehubaViewerTouch.directive.ts
+++ b/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerTouch.directive.ts
@@ -1,6 +1,6 @@
-import { Directive, ElementRef, Input, HostListener, Output, OnDestroy } from "@angular/core";
+import { Directive, ElementRef, Input, OnDestroy } from "@angular/core";
 import { Observable, fromEvent, merge, Subscription } from "rxjs";
-import { map, filter, shareReplay, switchMap, pairwise, takeUntil, tap, switchMapTo } from "rxjs/operators";
+import { map, filter, shareReplay, switchMap, pairwise, takeUntil, switchMapTo } from "rxjs/operators";
 import { getExportNehuba } from 'src/util/fn'
 import { computeDistance } from "../nehubaViewer/nehubaViewer.component";
 
diff --git a/src/ui/nehubaContainer/statusCard/statusCard.component.spec.ts b/src/viewerModule/nehuba/statusCard/statusCard.component.spec.ts
similarity index 97%
rename from src/ui/nehubaContainer/statusCard/statusCard.component.spec.ts
rename to src/viewerModule/nehuba/statusCard/statusCard.component.spec.ts
index 8db6d1a4e195ec1c5f7cb08a6b163c4773d232e6..698b858476e4b330ef7c84904c518a893dcb8ff5 100644
--- a/src/ui/nehubaContainer/statusCard/statusCard.component.spec.ts
+++ b/src/viewerModule/nehuba/statusCard/statusCard.component.spec.ts
@@ -16,9 +16,10 @@ import { viewerConfigSelectorUseMobileUi } from "src/services/state/viewerConfig
 import { viewerStateNavigationStateSelector, viewerStateSelectedTemplatePureSelector } from "src/services/state/viewerState/selectors"
 import * as util from '../util'
 import { viewerStateChangeNavigation } from "src/services/state/viewerState/actions"
+import {QuickTourModule} from "src/ui/quickTour/module";
 
 @Directive({
-  selector: '[iav-auth-authState]',
+  selector: '[iav-auth-auth-state]',
   exportAs: 'iavAuthAuthState'
 })
 
@@ -46,6 +47,7 @@ describe('> statusCard.component.ts', () => {
           ReactiveFormsModule,
           NoopAnimationsModule,
           UtilModule,
+          QuickTourModule
         ],
         declarations: [
           StatusCardComponent,
@@ -88,13 +90,13 @@ describe('> statusCard.component.ts', () => {
       })
 
       it('> toggle can be found', () => {
-      
+
         const slider = fixture.debugElement.query( By.directive(MatSlideToggle) )
         expect(slider).toBeTruthy()
       })
-  
+
       it('> toggling voxel/real toggle also toggles statusPanelRealSpace flag', () => {
-  
+
         const prevFlag = fixture.componentInstance.statusPanelRealSpace
         const sliderEl = fixture.debugElement.query( By.directive(MatSlideToggle) )
         const slider = sliderEl.injector.get(MatSlideToggle)
@@ -102,21 +104,21 @@ describe('> statusCard.component.ts', () => {
         fixture.detectChanges()
         expect(fixture.componentInstance.statusPanelRealSpace).toEqual(!prevFlag)
       })
-  
+
       describe('> textNavigationTo', () => {
         it('> takes into account of statusPanelRealSpace panel', () => {
           const setNavigationStateSpy = jasmine.createSpy('setNavigationState')
           fixture.componentInstance.nehubaViewer = {
-            setNavigationState: setNavigationStateSpy
+            setNavigationState: setNavigationStateSpy,
           } as any
-  
+
           fixture.componentInstance.statusPanelRealSpace = true
           fixture.componentInstance.textNavigateTo('1, 0, 0')
           expect(setNavigationStateSpy).toHaveBeenCalledWith({
             position: [1e6, 0, 0],
             positionReal: true
           })
-  
+
           fixture.componentInstance.statusPanelRealSpace = false
           fixture.componentInstance.textNavigateTo('1, 0, 0')
           expect(setNavigationStateSpy).toHaveBeenCalledWith({
@@ -152,9 +154,9 @@ describe('> statusCard.component.ts', () => {
         const mockStore = TestBed.inject(MockStore)
         mockStore.overrideSelector(viewerStateSelectedTemplatePureSelector, mockTemplate)
         mockStore.overrideSelector(viewerStateNavigationStateSelector, mockCurrNavigation)
-        
+
         spyOnProperty(util, 'getNavigationStateFromConfig').and.returnValue(getNavigationStateFromConfigSpy)
-        
+
         fixture = TestBed.createComponent(StatusCardComponent)
         fixture.detectChanges()
         fixture.componentInstance.showFull = true
@@ -176,7 +178,7 @@ describe('> statusCard.component.ts', () => {
             const idspatchSpy = spyOn(mockStore, 'dispatch')
             fixture.componentInstance.resetNavigation({ [method]: true,  })
             fixture.detectChanges()
-            
+
             const overrideObj = {}
             if (method === 'rotation') overrideObj['orientation'] = mockNavState['orientation']
             if (method === 'position') overrideObj['position'] = mockNavState['position']
@@ -195,7 +197,7 @@ describe('> statusCard.component.ts', () => {
         })
       }
     })
-  
-    
+
+
   })
 })
diff --git a/src/ui/nehubaContainer/statusCard/statusCard.component.ts b/src/viewerModule/nehuba/statusCard/statusCard.component.ts
similarity index 65%
rename from src/ui/nehubaContainer/statusCard/statusCard.component.ts
rename to src/viewerModule/nehuba/statusCard/statusCard.component.ts
index 8f14652f75394e561bf700fa7a26f006d1129da5..a6daa233d6acdc5197d19d4a9dbc284c3e26f329 100644
--- a/src/ui/nehubaContainer/statusCard/statusCard.component.ts
+++ b/src/viewerModule/nehuba/statusCard/statusCard.component.ts
@@ -1,28 +1,43 @@
-import { Component, Input, OnInit, OnChanges, TemplateRef, HostBinding } from "@angular/core";
+import {
+  Component,
+  OnInit,
+  OnChanges,
+  TemplateRef,
+  HostBinding,
+  Optional,
+  Inject,
+} from "@angular/core";
 import { select, Store } from "@ngrx/store";
 import { LoggingService } from "src/logging";
-import { IavRootStoreInterface } from "src/services/stateStore.service";
 import { NehubaViewerUnit } from "../nehubaViewer/nehubaViewer.component";
 import { Observable, Subscription, of, combineLatest } from "rxjs";
 import { map, filter, startWith } from "rxjs/operators";
 import { MatBottomSheet } from "@angular/material/bottom-sheet";
 import { MatDialog } from "@angular/material/dialog";
-import { ARIA_LABELS } from 'common/constants'
-import { PureContantService } from "src/util";
+import { ARIA_LABELS, QUICKTOUR_DESC } from 'common/constants'
 import { FormControl } from "@angular/forms";
 import { viewerStateNavigationStateSelector, viewerStateSelectedTemplatePureSelector } from "src/services/state/viewerState/selectors";
-import { getNavigationStateFromConfig } from "../util";
+
 import { viewerStateChangeNavigation } from "src/services/state/viewerState/actions";
+import { getNavigationStateFromConfig, NEHUBA_INSTANCE_INJTKN } from '../util'
+import { IQuickTourData } from "src/ui/quickTour/constrants";
 
 @Component({
-  selector : 'ui-status-card',
+  selector : 'iav-cmp-viewer-nehuba-status',
   templateUrl : './statusCard.template.html',
   styleUrls : ['./statusCard.style.css'],
 })
 export class StatusCardComponent implements OnInit, OnChanges{
 
-  @Input() public selectedTemplateName: string;
-  @Input() public nehubaViewer: NehubaViewerUnit;
+  private _nehubaViewer: NehubaViewerUnit;
+
+  get nehubaViewer(){
+    return this._nehubaViewer
+  }
+  set nehubaViewer(v: NehubaViewerUnit) {
+    this._nehubaViewer = v
+    this.ngOnChanges()
+  }
 
   @HostBinding('attr.aria-label')
   public arialabel = ARIA_LABELS.STATUS_PANEL
@@ -37,6 +52,11 @@ export class StatusCardComponent implements OnInit, OnChanges{
 
   public useTouchInterface$: Observable<boolean>
 
+  public quickTourData: IQuickTourData = {
+    description: QUICKTOUR_DESC.STATUS_CARD,
+    order: 6,
+  }
+
   public SHARE_BTN_ARIA_LABEL = ARIA_LABELS.SHARE_BTN
   public COPY_URL_TO_CLIPBOARD_ARIA_LABEL = ARIA_LABELS.SHARE_COPY_URL_CLIPBOARD
   public SHARE_CUSTOM_URL_ARIA_LABEL = ARIA_LABELS.SHARE_CUSTOM_URL
@@ -44,13 +64,23 @@ export class StatusCardComponent implements OnInit, OnChanges{
   public SHOW_FULL_STATUS_PANEL_ARIA_LABEL = ARIA_LABELS.SHOW_FULL_STATUS_PANEL
   public HIDE_FULL_STATUS_PANEL_ARIA_LABEL = ARIA_LABELS.HIDE_FULL_STATUS_PANEL
   constructor(
-    private store$: Store<IavRootStoreInterface>,
+    private store$: Store<any>,
     private log: LoggingService,
     private bottomSheet: MatBottomSheet,
     private dialog: MatDialog,
-    private pureConstantService: PureContantService
+    @Optional() @Inject(NEHUBA_INSTANCE_INJTKN) nehubaViewer$: Observable<NehubaViewerUnit>
   ) {
-    this.useTouchInterface$ = this.pureConstantService.useTouchUI$
+    this.useTouchInterface$ = of(true) //this.pureConstantService.useTouchUI$
+
+    if (nehubaViewer$) {
+      this.subscriptions.push(
+        nehubaViewer$.subscribe(
+          viewer => this.nehubaViewer = viewer
+        )
+      )
+    } else {
+      this.log.warn(`NEHUBA_INSTANCE_INJTKN not injected!`)
+    }
   }
 
   ngOnInit(): void {
@@ -74,40 +104,45 @@ export class StatusCardComponent implements OnInit, OnChanges{
   }
 
   ngOnChanges() {
-    if (!this.nehubaViewer) {
+    if (this.nehubaViewer?.viewerPosInReal$ && this.nehubaViewer?.viewerPosInVoxel$) {
+      this.navVal$ = combineLatest([
+        this.statusPanelRealSpace$,
+        this.nehubaViewer.viewerPosInReal$.pipe(
+          filter(v => !!v)
+        ),
+        this.nehubaViewer.viewerPosInVoxel$.pipe(
+          filter(v => !!v)
+        )
+      ]).pipe(
+        map(([realFlag, real, voxel]) => realFlag
+          ? real.map(v => `${ (v / 1e6).toFixed(3) }mm`).join(', ')
+          : voxel.map(v => v.toFixed(3)).join(', ') ),
+        startWith(`nehubaViewer initialising`)
+      )
+    } else {
       this.navVal$ = of(`neubaViewer is undefined`)
-      this.mouseVal$ = of(`neubaViewer is undefined`)
-      return
     }
-    this.navVal$ = combineLatest([
-      this.statusPanelRealSpace$,
-      this.nehubaViewer.viewerPosInReal$.pipe(
-        filter(v => !!v)
-      ),
-      this.nehubaViewer.viewerPosInVoxel$.pipe(
-        filter(v => !!v)
-      )
-    ]).pipe(
-      map(([realFlag, real, voxel]) => realFlag
-        ? real.map(v => `${ (v / 1e6).toFixed(3) }mm`).join(', ')
-        : voxel.map(v => v.toFixed(3)).join(', ') ),
-      startWith(`nehubaViewer initialising`)
-    )
 
-    this.mouseVal$ = combineLatest([
-      this.statusPanelRealSpace$,
-      this.nehubaViewer.mousePosInReal$.pipe(
-        filter(v => !!v)
-      ),
-      this.nehubaViewer.mousePosInVoxel$.pipe(
-        filter(v => !!v)
+    if ( this.nehubaViewer?.mousePosInReal$ && this.nehubaViewer?.mousePosInVoxel$ ) {
+
+      this.mouseVal$ = combineLatest([
+        this.statusPanelRealSpace$,
+        this.nehubaViewer.mousePosInReal$.pipe(
+          filter(v => !!v)
+        ),
+        this.nehubaViewer.mousePosInVoxel$.pipe(
+          filter(v => !!v)
+        )
+      ]).pipe(
+        map(([realFlag, real, voxel]) => realFlag
+          ? real.map(v => `${ (v/1e6).toFixed(3) }mm`).join(', ')
+          : voxel.map(v => v.toFixed(3)).join(', ')),
+        startWith(``)
       )
-    ]).pipe(
-      map(([realFlag, real, voxel]) => realFlag
-        ? real.map(v => `${ (v/1e6).toFixed(3) }mm`).join(', ')
-        : voxel.map(v => v.toFixed(3)).join(', ')),
-      startWith(``)
-    )
+    } else {
+      this.mouseVal$ = of(`neubaViewer is undefined`)
+    }
+
   }
 
   public statusPanelFormCtrl = new FormControl(true, [])
@@ -144,8 +179,6 @@ export class StatusCardComponent implements OnInit, OnChanges{
   public resetNavigation({rotation: rotationFlag = false, position: positionFlag = false, zoom : zoomFlag = false}: {rotation?: boolean, position?: boolean, zoom?: boolean}) {
     const {
       orientation,
-      perspectiveOrientation,
-      perspectiveZoom,
       position,
       zoom
     } = getNavigationStateFromConfig(this.selectedTemplatePure.nehubaConfig)
diff --git a/src/ui/nehubaContainer/statusCard/statusCard.style.css b/src/viewerModule/nehuba/statusCard/statusCard.style.css
similarity index 100%
rename from src/ui/nehubaContainer/statusCard/statusCard.style.css
rename to src/viewerModule/nehuba/statusCard/statusCard.style.css
diff --git a/src/ui/nehubaContainer/statusCard/statusCard.template.html b/src/viewerModule/nehuba/statusCard/statusCard.template.html
similarity index 54%
rename from src/ui/nehubaContainer/statusCard/statusCard.template.html
rename to src/viewerModule/nehuba/statusCard/statusCard.template.html
index 17d4fe7cfd9f6c5c18cedd1f3471b320934b1e62..5e4d793a725e31f41f5bd45406a11b95ef1340a6 100644
--- a/src/ui/nehubaContainer/statusCard/statusCard.template.html
+++ b/src/viewerModule/nehuba/statusCard/statusCard.template.html
@@ -1,108 +1,115 @@
-<mat-card class="expandedContainer p-2 pt-1" *ngIf="showFull; else showMin">
-  <mat-card-content>
+<div quick-tour
+  [quick-tour-description]="quickTourData.description"
+  [quick-tour-order]="quickTourData.order"
+  #statusCardQT="quickTour">
+  <mat-card *ngIf="showFull; else showMin"
+    class="expandedContainer p-2 pt-1">
+    
+    <mat-card-content>
 
-    <!-- reset -->
-    <div class="d-flex">
-      <span class="flex-grow-0 d-flex align-items-center">
-        Reset
-      </span>
+      <!-- reset -->
+      <div class="d-flex">
+        <span class="flex-grow-0 d-flex align-items-center">
+          Reset
+        </span>
 
-      <div class="flex-grow-1"></div>
-
-      <button
-        mat-icon-button
-        (click)="resetNavigation({position:true})"
-        matTooltip="Reset position">
-        <i class="iavic iavic-translation"></i>
-      </button>
-
-      <button
-        mat-icon-button
-        (click)="resetNavigation({rotation:true})"
-        matTooltip="Reset rotation">
-        <i class="iavic iavic-rotation"></i>
-      </button>
-
-      <button
-        mat-icon-button
-        (click)="resetNavigation({zoom:true})"
-        matTooltip="Reset zoom">
-        <i class="iavic iavic-scaling"></i>
-      </button>
-
-      <mat-divider [vertical]="true"></mat-divider>
-
-      <button mat-icon-button
-        [attr.aria-label]="HIDE_FULL_STATUS_PANEL_ARIA_LABEL"
-        (click)="showFull = false">
-        <i class="fas fa-angle-up"></i>
-      </button>
-    </div>
+        <div class="flex-grow-1"></div>
 
-    <!-- space -->
-    <div class="d-flex">
-      <span class="d-flex align-items-center">
-        Voxel space
-      </span>
+        <button
+          mat-icon-button
+          (click)="resetNavigation({position:true})"
+          matTooltip="Reset position">
+          <i class="iavic iavic-translation"></i>
+        </button>
 
-      <mat-slide-toggle
-        [formControl]="statusPanelFormCtrl"
-        class="pl-2 pr-2">
-      </mat-slide-toggle>
-      
-      <span class="d-flex align-items center">
-        Physical space
-      </span>
-    </div>
+        <button
+          mat-icon-button
+          (click)="resetNavigation({rotation:true})"
+          matTooltip="Reset rotation">
+          <i class="iavic iavic-rotation"></i>
+        </button>
+
+        <button
+          mat-icon-button
+          (click)="resetNavigation({zoom:true})"
+          matTooltip="Reset zoom">
+          <i class="iavic iavic-scaling"></i>
+        </button>
+
+        <mat-divider [vertical]="true"></mat-divider>
+
+        <button mat-icon-button
+          [attr.aria-label]="HIDE_FULL_STATUS_PANEL_ARIA_LABEL"
+          (click)="statusCardQT.ngOnChanges(); showFull = false">
+          <i class="fas fa-angle-up"></i>
+        </button>
+      </div>
+
+      <!-- space -->
+      <div class="d-flex">
+        <span class="d-flex align-items-center">
+          Voxel space
+        </span>
+
+        <mat-slide-toggle
+          [formControl]="statusPanelFormCtrl"
+          class="pl-2 pr-2">
+        </mat-slide-toggle>
 
-    <!-- coord -->
-    <div class="d-flex">
+        <span class="d-flex align-items center">
+          Physical space
+        </span>
+      </div>
 
-      <mat-form-field class="flex-grow-1">
+      <!-- coord -->
+      <div class="d-flex">
+
+        <mat-form-field class="flex-grow-1">
+          <mat-label>
+            {{ (statusPanelRealSpace$ | async) ? 'Physical Coord' : 'Voxel Coord' }}
+          </mat-label>
+          <input type="text"
+            matInput
+            (keydown.enter)="textNavigateTo(navInput.value)"
+            (keydown.tab)="textNavigateTo(navInput.value)"
+            [value]="navVal$ | async"
+            #navInput="matInput">
+
+        </mat-form-field>
+
+        <div class="w-0 position-relative">
+          <button
+            (click)="showBottomSheet(shareTmpl)"
+            [attr.aria-label]="SHARE_BTN_ARIA_LABEL"
+            mat-icon-button
+            class="position-absolute share-btn">
+            <i class="fas fa-share-square"></i>
+          </button>
+        </div>
+      </div>
+
+      <!-- cursor pos -->
+      <mat-form-field *ngIf="!(useTouchInterface$ | async)"
+        class="w-100">
         <mat-label>
-          {{ (statusPanelRealSpace$ | async) ? 'Physical Coord' : 'Voxel Coord' }}
+          Cursor Position
         </mat-label>
         <input type="text"
           matInput
-          (keydown.enter)="textNavigateTo(navInput.value)"
-          (keydown.tab)="textNavigateTo(navInput.value)"
-          [value]="navVal$ | async"
-          #navInput="matInput">
-  
+          [readonly]="true"
+          [value]="mouseVal$ | async">
       </mat-form-field>
 
-      <div class="w-0 position-relative">
-        <button
-          (click)="showBottomSheet(shareTmpl)"
-          [attr.aria-label]="SHARE_BTN_ARIA_LABEL"
-          mat-icon-button
-          class="position-absolute share-btn">
-          <i class="fas fa-share-square"></i>
-        </button>
-      </div>
-    </div>
-
-    <!-- cursor pos -->
-    <mat-form-field *ngIf="!(useTouchInterface$ | async)"
-      class="w-100">
-      <mat-label>
-        Cursor Position
-      </mat-label>
-      <input type="text"
-        matInput
-        [readonly]="true"
-        [value]="mouseVal$ | async">
-    </mat-form-field>
-
-  </mat-card-content>
-</mat-card>
+    </mat-card-content>
+  </mat-card>
+</div>
 
 <!-- minimised status bar -->
 <ng-template #showMin>
   <div class="iv-custom-comp text overflow-visible text-nowrap d-inline-flex align-items-center m-1 mt-3"
     iav-media-query
     #media="iavMediaQuery">
-    
+
     <i aria-label="viewer navigation" class="fas fa-compass"></i>
     <span *ngIf="(media.mediaBreakPoint$ | async) < 3" class="pl-2">
       {{ navVal$ | async }}
@@ -123,7 +130,7 @@
 
     <button mat-icon-button
       [attr.aria-label]="SHOW_FULL_STATUS_PANEL_ARIA_LABEL"
-      (click)="showFull = true">
+      (click)="statusCardQT.ngOnChanges(); showFull = true">
       <i class="fas fa-angle-down"></i>
     </button>
   </div>
@@ -150,12 +157,12 @@
     <mat-list-item (click)="openDialog(shareSaneUrl, { ariaLabel: SHARE_CUSTOM_URL_DIALOG_ARIA_LABEL })"
       [attr.aria-label]="SHARE_CUSTOM_URL_ARIA_LABEL"
       [attr.tab-index]="10">
-      <mat-icon 
+      <mat-icon
         class="mr-4"
         fontSet="fas"
         fontIcon="fa-link">
       </mat-icon>
-      
+
       <span>
         Create custom URL
       </span>
@@ -170,7 +177,7 @@
   </h2>
 
   <div mat-dialog-content>
-    <div iav-auth-authState
+    <div iav-auth-auth-state
       #authState="iavAuthAuthState">
 
       <!-- Logged in. Explain that links will not expire, offer to logout -->
diff --git a/src/viewerModule/nehuba/store/actions.ts b/src/viewerModule/nehuba/store/actions.ts
new file mode 100644
index 0000000000000000000000000000000000000000..38c6572e1259651318bede3684278e71179f3daa
--- /dev/null
+++ b/src/viewerModule/nehuba/store/actions.ts
@@ -0,0 +1,36 @@
+import { createAction, props } from "@ngrx/store";
+import { INgLayerInterface } from "src/services/state/ngViewerState.store";
+import { NEHUBA_VIEWER_FEATURE_KEY } from "../constants";
+import { IAuxMesh } from "./type";
+
+export const actionAddNgLayer = createAction(
+  `[${NEHUBA_VIEWER_FEATURE_KEY}] [addNgLayer]`,
+  props<{
+    layers: INgLayerInterface[]
+  }>()
+)
+
+export const actionSetAuxMesh = createAction(
+  `[${NEHUBA_VIEWER_FEATURE_KEY}] [setAuxMesh]`,
+  props<{
+    payload: IAuxMesh
+  }>()
+)
+
+export const actionRemoveAuxMesh = createAction(
+  `[${NEHUBA_VIEWER_FEATURE_KEY}] [rmAuxMesh]`,
+  props<{
+    payload: { "@id": string }
+  }>()
+)
+
+export const actionSetAuxMeshes = createAction(
+  `[${NEHUBA_VIEWER_FEATURE_KEY}] [setAuxMeshes]`,
+  props<{
+    payload: IAuxMesh[]
+  }>()
+)
+
+export const actionClearAuxMeshes = createAction(
+  `[${NEHUBA_VIEWER_FEATURE_KEY}] [clearAuxMeshes]`
+)
diff --git a/src/viewerModule/nehuba/store/index.ts b/src/viewerModule/nehuba/store/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a384638d8bf82bd1b38296d9a061405b552cd7f1
--- /dev/null
+++ b/src/viewerModule/nehuba/store/index.ts
@@ -0,0 +1,18 @@
+export {
+  actionAddNgLayer,
+  actionRemoveAuxMesh,
+  actionSetAuxMesh,
+  actionClearAuxMeshes,
+  actionSetAuxMeshes,
+} from './actions'
+export {
+  selectorAuxMeshes
+} from './selectors'
+export {
+  reducer
+} from './store'
+export {
+  IAuxMesh,
+  INehubaFeature,
+  INgLayerInterface
+} from './type'
diff --git a/src/viewerModule/nehuba/store/selectors.ts b/src/viewerModule/nehuba/store/selectors.ts
new file mode 100644
index 0000000000000000000000000000000000000000..528d54d7d49dc53dde74f5c59b280efb87b4db0b
--- /dev/null
+++ b/src/viewerModule/nehuba/store/selectors.ts
@@ -0,0 +1,8 @@
+import { createSelector } from "@ngrx/store";
+import { NEHUBA_VIEWER_FEATURE_KEY } from '../constants'
+import { INehubaFeature, IAuxMesh } from "./type";
+
+export const selectorAuxMeshes = createSelector<any, INehubaFeature, IAuxMesh[]>(
+  state => state[NEHUBA_VIEWER_FEATURE_KEY],
+  nehubaFeatureStore => nehubaFeatureStore['auxMeshes']
+)
diff --git a/src/viewerModule/nehuba/store/store.ts b/src/viewerModule/nehuba/store/store.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6782fa77c723fe8457f268df1d74f63535556b58
--- /dev/null
+++ b/src/viewerModule/nehuba/store/store.ts
@@ -0,0 +1,54 @@
+import { createReducer, on } from "@ngrx/store";
+import { actionRemoveAuxMesh, actionSetAuxMesh, actionSetAuxMeshes } from "./actions";
+import { INehubaFeature } from "./type";
+
+
+/**
+ * TODO port from global store to feature store
+ */
+
+enum EnumPanelMode {
+  FOUR_PANEL = 'FOUR_PANEL',
+  V_ONE_THREE = 'V_ONE_THREE',
+  H_ONE_THREE = 'H_ONE_THREE',
+  SINGLE_PANEL = 'SINGLE_PANEL',
+}
+
+const defaultState: INehubaFeature = {
+  layers: [],
+  panelMode: EnumPanelMode.FOUR_PANEL,
+  panelOrder: '0123',
+  octantRemoval: true,
+  clearViewQueue: {},
+  auxMeshes: []
+}
+
+export const reducer = createReducer(
+  defaultState,
+  on(actionSetAuxMeshes, (state, { payload }) => {
+    return {
+      ...state,
+      auxMeshes: payload
+    }
+  }),
+  on(actionSetAuxMesh, (state, { payload }) => {
+    return {
+      ...state,
+      auxMeshes: state.auxMeshes.map(v => v['@id'] === payload['@id']
+        ? payload
+        : v)
+    }
+  }),
+  on(actionRemoveAuxMesh, (state, {payload}) => {
+    return {
+      ...state,
+      auxMeshes: state.auxMeshes.filter(v => v['@id'] !== payload['@id'])
+    }
+  }),
+  on(actionRemoveAuxMesh, state => {
+    return {
+      ...state,
+      auxMeshes: []
+    }
+  })
+)
diff --git a/src/viewerModule/nehuba/store/type.ts b/src/viewerModule/nehuba/store/type.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4b1d4d3d69ab657aa67edbd18512397cda07d495
--- /dev/null
+++ b/src/viewerModule/nehuba/store/type.ts
@@ -0,0 +1,31 @@
+export interface IAuxMesh {
+  ['@id']: string
+  name: string
+  displayName?: string
+  ngId: string
+  labelIndicies: number[]
+  rgb: [number, number, number]
+  visible: boolean
+}
+
+export interface INgLayerInterface {
+  name: string // displayName
+  source: string
+  mixability: string // base | mixable | nonmixable
+  annotation?: string //
+  id?: string // unique identifier
+  visible?: boolean
+  shader?: string
+  transform?: any
+}
+
+export interface INehubaFeature {
+  layers: INgLayerInterface[]
+  panelMode: string
+  panelOrder: string
+  octantRemoval: boolean
+  clearViewQueue: {
+    [key: string]: boolean
+  }
+  auxMeshes: IAuxMesh[]
+}
diff --git a/src/ui/nehubaContainer/touchSideClass.directive.ts b/src/viewerModule/nehuba/touchSideClass.directive.ts
similarity index 93%
rename from src/ui/nehubaContainer/touchSideClass.directive.ts
rename to src/viewerModule/nehuba/touchSideClass.directive.ts
index edaa9823ff6af39756e0f51e16c91cee40d91022..44cdac937990acc2b79bb03a70d545fb8030af1b 100644
--- a/src/ui/nehubaContainer/touchSideClass.directive.ts
+++ b/src/viewerModule/nehuba/touchSideClass.directive.ts
@@ -4,7 +4,8 @@ import { Observable, Subscription } from "rxjs";
 import { distinctUntilChanged, tap } from "rxjs/operators";
 import { ngViewerSelectorPanelMode } from "src/services/state/ngViewerState/selectors";
 import { IavRootStoreInterface } from "src/services/stateStore.service";
-import { addTouchSideClasses, removeTouchSideClasses } from "./util";
+import { addTouchSideClasses, removeTouchSideClasses } from "src/viewerModule/nehuba/util";
+
 
 @Directive({
   selector: '[touch-side-class]',
diff --git a/src/viewerModule/nehuba/types.ts b/src/viewerModule/nehuba/types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..35cca295acae535d68ba6bb98ce282650c830e45
--- /dev/null
+++ b/src/viewerModule/nehuba/types.ts
@@ -0,0 +1,13 @@
+import { INavObj } from "./navigation.service";
+
+export type TNehubaContextInfo = {
+  nav: INavObj
+  mouse: {
+    real: number[]
+    voxel: number[]
+  }
+  nehuba: {
+    layerName: string
+    labelIndices: number[]
+  }[]
+}
diff --git a/src/viewerModule/nehuba/util.spec.ts b/src/viewerModule/nehuba/util.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5b23c2d66959627b08af169d3aed8589c42018b8
--- /dev/null
+++ b/src/viewerModule/nehuba/util.spec.ts
@@ -0,0 +1,119 @@
+import { cvtNavigationObjToNehubaConfig } from './util'
+const bigbrainJson = require('!json-loader!src/res/ext/bigbrain.json')
+const bigBrainNehubaConfig = require('!json-loader!src/res/ext/bigbrainNehubaConfig.json')
+const reconstitutedBigBrain = JSON.parse(JSON.stringify(
+  {
+    ...bigbrainJson,
+    nehubaConfig: bigBrainNehubaConfig
+  }
+))
+const currentNavigation = {
+  position: [4, 5, 6],
+  orientation: [0, 0, 0, 1],
+  perspectiveOrientation: [ 0, 0, 0, 1],
+  perspectiveZoom: 2e5,
+  zoom: 1e5
+}
+
+const defaultPerspectiveZoom = 1e6
+const defaultZoom = 1e6
+
+const defaultNavigationObject = {
+  orientation: [0, 0, 0, 1],
+  perspectiveOrientation: [0 , 0, 0, 1],
+  perspectiveZoom: defaultPerspectiveZoom,
+  zoom: defaultZoom,
+  position: [0, 0, 0],
+  positionReal: true
+}
+
+const defaultNehubaConfigObject = {
+  perspectiveOrientation: [0, 0, 0, 1],
+  perspectiveZoom: 1e6,
+  navigation: {
+    pose: {
+      position: {
+        voxelCoordinates: [0, 0, 0],
+        voxelSize: [1,1,1]
+      },
+      orientation: [0, 0, 0, 1],
+    },
+    zoomFactor: defaultZoom
+  }
+}
+
+describe('> util.ts', () => {
+  
+  describe('> cvtNavigationObjToNehubaConfig', () => {
+    const validNehubaConfigObj = reconstitutedBigBrain.nehubaConfig.dataset.initialNgState
+    const validNavigationObj = currentNavigation
+    describe('> if inputs are malformed', () => {
+      describe('> if navigation object is malformed, uses navigation default object', () => {
+        it('> if navigation object is null', () => {
+          const v1 = cvtNavigationObjToNehubaConfig(null, validNehubaConfigObj)
+          const v2 = cvtNavigationObjToNehubaConfig(defaultNavigationObject, validNehubaConfigObj)
+          expect(v1).toEqual(v2)
+        })
+        it('> if navigation object is undefined', () => {
+          const v1 = cvtNavigationObjToNehubaConfig(undefined, validNehubaConfigObj)
+          const v2 = cvtNavigationObjToNehubaConfig(defaultNavigationObject, validNehubaConfigObj)
+          expect(v1).toEqual(v2)
+        })
+
+        it('> if navigation object is otherwise malformed', () => {
+          const v1 = cvtNavigationObjToNehubaConfig(reconstitutedBigBrain, validNehubaConfigObj)
+          const v2 = cvtNavigationObjToNehubaConfig(defaultNavigationObject, validNehubaConfigObj)
+          expect(v1).toEqual(v2)
+
+          const v3 = cvtNavigationObjToNehubaConfig({}, validNehubaConfigObj)
+          const v4 = cvtNavigationObjToNehubaConfig(defaultNavigationObject, validNehubaConfigObj)
+          expect(v3).toEqual(v4)
+        })
+      })
+
+      describe('> if nehubaConfig object is malformed, use default nehubaConfig obj', () => {
+        it('> if nehubaConfig is null', () => {
+          const v1 = cvtNavigationObjToNehubaConfig(validNavigationObj, null)
+          const v2 = cvtNavigationObjToNehubaConfig(validNavigationObj, defaultNehubaConfigObject)
+          expect(v1).toEqual(v2)
+        })
+
+        it('> if nehubaConfig is undefined', () => {
+          const v1 = cvtNavigationObjToNehubaConfig(validNavigationObj, undefined)
+          const v2 = cvtNavigationObjToNehubaConfig(validNavigationObj, defaultNehubaConfigObject)
+          expect(v1).toEqual(v2)
+        })
+
+        it('> if nehubaConfig is otherwise malformed', () => {
+          const v1 = cvtNavigationObjToNehubaConfig(validNavigationObj, {})
+          const v2 = cvtNavigationObjToNehubaConfig(validNavigationObj, defaultNehubaConfigObject)
+          expect(v1).toEqual(v2)
+
+          const v3 = cvtNavigationObjToNehubaConfig(validNavigationObj, reconstitutedBigBrain)
+          const v4 = cvtNavigationObjToNehubaConfig(validNavigationObj, defaultNehubaConfigObject)
+          expect(v3).toEqual(v4)
+        })
+      })
+    })
+    it('> converts navigation object and reference nehuba config object to navigation object', () => {
+      const convertedVal = cvtNavigationObjToNehubaConfig(validNavigationObj, validNehubaConfigObj)
+      const { perspectiveOrientation, orientation, zoom, perspectiveZoom, position } = validNavigationObj
+      
+      expect(convertedVal).toEqual({
+        navigation: {
+          pose: {
+            position: {
+              voxelSize: validNehubaConfigObj.navigation.pose.position.voxelSize,
+              voxelCoordinates: [0, 1, 2].map(idx => position[idx] / validNehubaConfigObj.navigation.pose.position.voxelSize[idx])
+            },
+            orientation
+          },
+          zoomFactor: zoom
+        },
+        perspectiveOrientation: perspectiveOrientation,
+        perspectiveZoom: perspectiveZoom
+      })
+    })
+  })
+
+})
diff --git a/src/ui/nehubaContainer/util.ts b/src/viewerModule/nehuba/util.ts
similarity index 87%
rename from src/ui/nehubaContainer/util.ts
rename to src/viewerModule/nehuba/util.ts
index 49d7136412e6d19d67bdf6774d7e6f81e9832988..398ad2d82aafbbdafc6c8b8c50d72239a91ded92 100644
--- a/src/ui/nehubaContainer/util.ts
+++ b/src/viewerModule/nehuba/util.ts
@@ -1,7 +1,9 @@
-import { pipe } from 'rxjs'
+import { InjectionToken } from '@angular/core'
+import { Observable, pipe } from 'rxjs'
 import { filter, scan, take } from 'rxjs/operators'
 import { PANELS } from 'src/services/state/ngViewerState.store.helper'
 import { getViewer } from 'src/util/fn'
+import { NehubaViewerUnit } from './nehubaViewer/nehubaViewer.component'
 
 const flexContCmnCls = ['w-100', 'h-100', 'd-flex', 'justify-content-center', 'align-items-stretch']
 
@@ -285,3 +287,43 @@ export const takeOnePipe = () => {
     take(1),
   )
 }
+
+export const NEHUBA_INSTANCE_INJTKN = new InjectionToken<Observable<NehubaViewerUnit>>('NEHUBA_INSTANCE_INJTKN')
+
+export function cvtNavigationObjToNehubaConfig(navigationObj, nehubaConfigObj){
+  const {
+    orientation = [0, 0, 0, 1],
+    perspectiveOrientation = [0, 0, 0, 1],
+    perspectiveZoom = 1e6,
+    zoom = 1e6,
+    position = [0, 0, 0],
+    positionReal = true,
+  } = navigationObj || {}
+
+  const voxelSize = (() => {
+    const {
+      navigation = {}
+    } = nehubaConfigObj || {}
+    const { pose = {} } = navigation
+    const { position = {} } = pose
+    const { voxelSize = [1, 1, 1] } = position
+    return voxelSize
+  })()
+
+  return {
+    perspectiveOrientation,
+    perspectiveZoom,
+    navigation: {
+      pose: {
+        position: {
+          voxelCoordinates: positionReal
+            ? [0, 1, 2].map(idx => position[idx] / voxelSize[idx])
+            : position,
+          voxelSize
+        },
+        orientation,
+      },
+      zoomFactor: zoom
+    }
+  }
+}
diff --git a/src/viewerModule/nehuba/viewerCtrl/index.ts b/src/viewerModule/nehuba/viewerCtrl/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6f3447caf89d780f37adf4a2ff01403e9468440a
--- /dev/null
+++ b/src/viewerModule/nehuba/viewerCtrl/index.ts
@@ -0,0 +1 @@
+export { ViewerCtrlModule } from './module'
diff --git a/src/viewerCtrl/module.ts b/src/viewerModule/nehuba/viewerCtrl/module.ts
similarity index 60%
rename from src/viewerCtrl/module.ts
rename to src/viewerModule/nehuba/viewerCtrl/module.ts
index dd1a100beeba314400417dc5ac19670bf090bde4..e3f1623f13f128fbc5df4ba9bd88606048bda0ae 100644
--- a/src/viewerCtrl/module.ts
+++ b/src/viewerModule/nehuba/viewerCtrl/module.ts
@@ -1,14 +1,19 @@
 import { CommonModule } from "@angular/common";
 import { NgModule } from "@angular/core";
+import { FormsModule, ReactiveFormsModule } from "@angular/forms";
+import { ComponentsModule } from "src/components";
 import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module";
+import { UtilModule } from "src/util";
 import { ViewerCtrlCmp } from "./viewerCtrlCmp/viewerCtrlCmp.component";
 
-// Migrate to viewer specific submodule when merged to dev
-
 @NgModule({
   imports: [
     CommonModule,
     AngularMaterialModule,
+    UtilModule,
+    FormsModule,
+    ReactiveFormsModule,
+    ComponentsModule,
   ],
   declarations: [
     ViewerCtrlCmp,
@@ -18,4 +23,4 @@ import { ViewerCtrlCmp } from "./viewerCtrlCmp/viewerCtrlCmp.component";
   ]
 })
 
-export class ViewerCtrlModule{}
\ No newline at end of file
+export class ViewerCtrlModule{}
diff --git a/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.component.spec.ts b/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3a3e81dc4cb133762ca00310d7e839904776fcd8
--- /dev/null
+++ b/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.component.spec.ts
@@ -0,0 +1,211 @@
+import { CommonModule } from "@angular/common"
+import { ComponentFixture, TestBed } from "@angular/core/testing"
+import { FormsModule, ReactiveFormsModule } from "@angular/forms"
+import { MockStore, provideMockStore } from "@ngrx/store/testing"
+import { BehaviorSubject, of } from "rxjs"
+import { ComponentsModule } from "src/components"
+import { ngViewerSelectorOctantRemoval } from "src/services/state/ngViewerState.store.helper"
+import { viewerStateCustomLandmarkSelector, viewerStateSelectedTemplatePureSelector } from "src/services/state/viewerState/selectors"
+import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module"
+import { UtilModule } from "src/util"
+import { actionSetAuxMeshes, selectorAuxMeshes } from "../../store"
+import { NEHUBA_INSTANCE_INJTKN } from "../../util"
+import { ViewerCtrlCmp } from "./viewerCtrlCmp.component"
+import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'
+import { HarnessLoader } from "@angular/cdk/testing"
+import { MatSlideToggleHarness } from '@angular/material/slide-toggle/testing'
+
+describe('> viewerCtrlCmp.component.ts', () => {
+  describe('> ViewerCtrlCmp', () => {
+    let fixture: ComponentFixture<ViewerCtrlCmp>
+    let loader: HarnessLoader
+    let mockStore: MockStore
+    let mockNehubaViewer = {
+      updateUserLandmarks: jasmine.createSpy()
+    }
+
+    afterEach(() => {
+      mockNehubaViewer.updateUserLandmarks.calls.reset()
+    })
+
+    beforeEach( async () => {
+      await TestBed.configureTestingModule({
+        imports: [
+          CommonModule,
+          AngularMaterialModule,
+          FormsModule,
+          ReactiveFormsModule,
+          ComponentsModule,
+          UtilModule,
+        ],
+        declarations: [ ViewerCtrlCmp ],
+        providers: [
+          provideMockStore(),
+          {
+            provide: NEHUBA_INSTANCE_INJTKN,
+            useValue: new BehaviorSubject(mockNehubaViewer)
+          }
+        ]
+      }).compileComponents()
+
+    })
+    beforeEach(() => {
+      mockStore = TestBed.inject(MockStore)
+      mockStore.overrideSelector(viewerStateSelectedTemplatePureSelector, {})
+      mockStore.overrideSelector(ngViewerSelectorOctantRemoval, true)
+      mockStore.overrideSelector(viewerStateCustomLandmarkSelector, [])
+    })
+
+    describe('> can be init', () => {
+
+      beforeEach(() => {
+        mockStore.overrideSelector(selectorAuxMeshes, [])
+        fixture = TestBed.createComponent(ViewerCtrlCmp)
+        fixture.detectChanges()
+        loader = TestbedHarnessEnvironment.loader(fixture)
+      })
+  
+      it('> can be inst', () => {
+        expect(fixture).toBeTruthy()
+      })
+    })
+
+    describe('> UI', () => {
+
+      beforeEach(() => {
+        mockStore.overrideSelector(selectorAuxMeshes, [])
+        fixture = TestBed.createComponent(ViewerCtrlCmp)
+        fixture.detectChanges()
+        loader = TestbedHarnessEnvironment.loader(fixture)
+      })
+
+      describe('> octant removal', () => {
+        const toggleName = 'remove-frontal-octant'
+        let setOctantRemovalSpy: jasmine.Spy
+        beforeEach(() => {
+          setOctantRemovalSpy = spyOn(fixture.componentInstance, 'setOctantRemoval')
+        })
+        afterEach(() => {
+          setOctantRemovalSpy.calls.reset()
+        })
+  
+        it('> toggleslider should exist', async () => {
+          const slideToggle = await loader.getAllHarnesses(
+            MatSlideToggleHarness.with({
+              name: toggleName,
+            })
+          )
+          expect(slideToggle.length).toBe(1)
+        })
+  
+        it('> toggling it should result in setOctantRemoval to be called', async () => {
+          const slideToggle = await loader.getAllHarnesses(
+            MatSlideToggleHarness.with({
+              name: toggleName,
+            })
+          )
+          const wasChecked = await slideToggle[0].isChecked()
+          await slideToggle[0].toggle()
+          expect(
+            setOctantRemovalSpy
+          ).toHaveBeenCalledWith(!wasChecked)
+        })
+      })
+  
+      describe('> toggle delineation', () => {
+        
+        let toggleDelination: jasmine.Spy
+        const toggleName = 'toggle-delineation'
+        beforeEach(() => {
+          toggleDelination = spyOn<any>(fixture.componentInstance, 'toggleParcVsbl')
+        })
+        afterEach(() => {
+          toggleDelination.calls.reset()
+        })
+  
+        it('> toggleslider should exist', async () => {
+          const slideToggle = await loader.getAllHarnesses(
+            MatSlideToggleHarness.with({
+              name: toggleName,
+            })
+          )
+          expect(slideToggle.length).toBe(1)
+        })
+  
+        it('> toggling it should result in setOctantRemoval to be called', async () => {
+          const slideToggle = await loader.getAllHarnesses(
+            MatSlideToggleHarness.with({
+              name: toggleName,
+            })
+          )
+          await slideToggle[0].toggle()
+          expect(
+            toggleDelination
+          ).toHaveBeenCalled()
+        })
+      })
+    })
+
+    describe('> UI aux meshes', () => {
+      const id = 'test-1-id'
+      const name = `toggle-aux-mesh-${id}`
+      const auxMesh = {
+        '@id': id,
+        labelIndicies: [1,2,3],
+        name: 'test-1-name',
+        ngId: 'test-1-ng-id',
+        rgb: [255, 255, 255] as [number, number, number] , 
+        visible: true,
+      }
+      const auxMesh2 = {
+        '@id': 'foo-bar',
+        labelIndicies: [3,4,5],
+        name: 'test-2-name',
+        ngId: 'test-2-ng-id',
+        rgb: [100, 100, 100] as [number, number, number] , 
+        visible: true,
+      }
+      beforeEach(() => {
+        mockStore.overrideSelector(selectorAuxMeshes, [
+          auxMesh,
+          auxMesh2
+        ])
+        fixture = TestBed.createComponent(ViewerCtrlCmp)
+        fixture.detectChanges()
+        loader = TestbedHarnessEnvironment.loader(fixture)
+      })
+      it('> toggleslider should exist', async () => {
+        const slideToggle = await loader.getAllHarnesses(
+          MatSlideToggleHarness.with({
+            name
+          })
+        )
+        expect(slideToggle.length).toBe(1)
+      })
+
+      it('> toggling it should call dispatch', async () => {
+        const dispatchSpy = spyOn(mockStore, 'dispatch')
+
+        const slideToggle = await loader.getAllHarnesses(
+          MatSlideToggleHarness.with({
+            name
+          })
+        )
+        await slideToggle[0].toggle()
+        expect(
+          dispatchSpy
+        ).toHaveBeenCalledWith(
+          actionSetAuxMeshes({
+            payload: [
+              {
+                ...auxMesh,
+                visible: !auxMesh.visible
+              },
+              auxMesh2
+            ]
+          })
+        )
+      })
+    })
+  })
+})
diff --git a/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.component.ts b/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cbe0380a6c5a06350b7ec29287072094343a25c2
--- /dev/null
+++ b/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.component.ts
@@ -0,0 +1,192 @@
+import { Component, HostBinding, Inject, Optional } from "@angular/core";
+import { select, Store } from "@ngrx/store";
+import { combineLatest, merge, Observable, of, Subscription } from "rxjs";
+import { filter, map, pairwise, withLatestFrom } from "rxjs/operators";
+import { ngViewerActionSetPerspOctantRemoval } from "src/services/state/ngViewerState/actions";
+import { ngViewerSelectorOctantRemoval } from "src/services/state/ngViewerState/selectors";
+import { viewerStateCustomLandmarkSelector, viewerStateSelectedTemplatePureSelector } from "src/services/state/viewerState/selectors";
+import { NehubaViewerUnit } from "src/viewerModule/nehuba";
+import { NEHUBA_INSTANCE_INJTKN } from "src/viewerModule/nehuba/util";
+import { ARIA_LABELS } from 'common/constants'
+import { actionSetAuxMeshes, selectorAuxMeshes } from "../../store";
+import { FormBuilder, FormControl, FormGroup } from "@angular/forms";
+
+@Component({
+  selector: 'viewer-ctrl-component',
+  templateUrl: './viewerCtrlCmp.template.html',
+  styleUrls: [
+    './viewerCtrlCmp.style.css'
+  ],
+  exportAs: 'viewerCtrlCmp'
+})
+
+export class ViewerCtrlCmp{
+
+  public ARIA_LABELS = ARIA_LABELS
+
+  @HostBinding('attr.darktheme')
+  darktheme = false
+
+  private _flagDelin = true
+  get flagDelin(){
+    return this._flagDelin
+  }
+  set flagDelin(flag){
+    this._flagDelin = flag
+    this.toggleParcVsbl()
+  }
+
+  private sub: Subscription[] = []
+  private hiddenLayerNames: string[] = []
+
+  private _removeOctantFlag: boolean
+  get removeOctantFlag(){
+    return this._removeOctantFlag
+  }
+  set removeOctantFlag(val){
+    this._removeOctantFlag = val
+    this.setOctantRemoval(this._removeOctantFlag)
+  }
+
+  public nehubaViewerPerspectiveOctantRemoval$ = this.store$.pipe(
+    select(ngViewerSelectorOctantRemoval),
+  )
+
+  public customLandmarks$: Observable<any> = this.store$.pipe(
+    select(viewerStateCustomLandmarkSelector),
+    map(lms => lms.map(lm => ({
+      ...lm,
+      geometry: {
+        position: lm.position
+      }
+    }))),
+  )
+
+  public auxMeshFormGroup: FormGroup
+  private auxMeshesNamesSet: Set<string> = new Set()
+  public auxMeshes$ = this.store$.pipe(
+    select(selectorAuxMeshes),
+  )
+
+  constructor(
+    private store$: Store<any>,
+    formBuilder: FormBuilder,
+    @Optional() @Inject(NEHUBA_INSTANCE_INJTKN) private nehubaInst$: Observable<NehubaViewerUnit>,
+  ){
+
+    this.auxMeshFormGroup = formBuilder.group({})
+  
+
+    if (this.nehubaInst$) {
+      this.sub.push(
+        combineLatest([
+          this.customLandmarks$,
+          this.nehubaInst$,
+        ]).pipe(
+          filter(([_, neubaInst]) => !!neubaInst),
+        ).subscribe(([landmarks, nehubainst]) => {
+          this.setOctantRemoval(landmarks.length === 0)
+          nehubainst.updateUserLandmarks(landmarks)
+        })
+      )
+    } else {
+      console.warn(`NEHUBA_INSTANCE_INJTKN not provided`)
+    }
+
+    this.sub.push(
+      this.store$.pipe(
+        select(viewerStateSelectedTemplatePureSelector)
+      ).subscribe(tmpl => {
+        const { useTheme } = tmpl || {}
+        this.darktheme = useTheme === 'dark'
+      }),
+
+      this.nehubaViewerPerspectiveOctantRemoval$.subscribe(
+        flag => this.removeOctantFlag = flag
+      ),
+
+      merge(
+        of(null),
+        this.auxMeshes$,
+      ).pipe(
+        pairwise()
+      ).subscribe(([oldMeshes, meshes]) => {
+        if (!!oldMeshes) {
+          for (const mesh of oldMeshes) {
+            this.auxMeshFormGroup.removeControl(mesh['@id'])
+          }
+        }
+        if (meshes === null) {
+          return
+        }
+        this.auxMeshesNamesSet.clear()
+        for (const mesh of meshes) {
+          this.auxMeshesNamesSet.add(mesh.ngId)
+          this.auxMeshFormGroup.addControl(mesh['@id'], new FormControl(mesh.visible))
+        }
+      }),
+
+      this.auxMeshFormGroup.valueChanges.pipe(
+        withLatestFrom(this.auxMeshes$)
+      ).subscribe(([v, auxMeshes]) => {
+        if (!auxMeshes) return
+
+        let changed = false
+        const auxMeshesCopy = JSON.parse(JSON.stringify(auxMeshes))
+        for (const key in v) {
+          const found = auxMeshesCopy.find(mesh => mesh['@id'] === key)
+          if (found && found.visible !== v[key]) {
+            changed = true
+            found.visible = v[key]
+          }
+        }
+
+        if (changed) {
+          this.store$.dispatch(
+            actionSetAuxMeshes({
+              payload: auxMeshesCopy
+            })
+          )
+        }
+      })
+    )
+  }
+
+  private toggleParcVsbl(){
+    const visibleParcLayers = ((window as any).viewer.layerManager.managedLayers)
+      .slice(1)
+      .filter(({ visible }) => visible)
+      .filter(layer => !this.auxMeshesNamesSet.has(layer.name))
+
+    if (this.flagDelin) {
+      for (const name of this.hiddenLayerNames) {
+        const l = (window as any).viewer.layerManager.getLayerByName(name)
+        l && l.setVisible(true)
+      }
+      this.hiddenLayerNames = []
+    } else {
+      this.hiddenLayerNames = []
+      for (const { name } of visibleParcLayers) {
+        const l = (window as any).viewer.layerManager.getLayerByName(name)
+        l && l.setVisible(false)
+        this.hiddenLayerNames.push( name )
+      }
+    }
+    
+    setTimeout(() => {
+      (window as any).viewer.display.scheduleRedraw()
+    })
+  }
+
+  public setOctantRemoval(octantRemovalFlag: boolean) {
+    this.store$.dispatch(
+      ngViewerActionSetPerspOctantRemoval({
+        octantRemovalFlag
+      })
+    )
+  }
+
+  public trackByAtId(_idx: number, obj: { ['@id']: string }) {
+    return obj['@id']
+  }
+}
diff --git a/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.style.css b/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.template.html b/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..a1a1507eedbdcf739c18c24f77642fb397d6df84
--- /dev/null
+++ b/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.template.html
@@ -0,0 +1,43 @@
+<h3 class="iv-custom-comp text mat-h3">
+  Volumes
+</h3>
+
+<mat-slide-toggle [(ngModel)]="flagDelin"
+  #delinToggle="matSlideToggle"
+  [iav-key-listener]="[{ type: 'keydown', key: 'q', target: 'document', capture: true }]"
+  (iav-key-event)="delinToggle.toggle()"
+  name="toggle-delineation">
+
+  <markdown-dom class="d-inline-block iv-custom-comp text">
+    Show delineations `[q]`
+  </markdown-dom>
+</mat-slide-toggle>
+
+<mat-divider class="mt-2 mb-2"></mat-divider>
+
+<h3 class="iv-custom-comp text mat-h3">
+  Perspective View
+</h3>
+
+<mat-slide-toggle [(ngModel)]="removeOctantFlag"
+  [aria-label]="ARIA_LABELS.TOGGLE_FRONTAL_OCTANT"
+  name="remove-frontal-octant">
+  <span class="iv-custom-comp text">
+    Remove frontal octant
+  </span>
+</mat-slide-toggle>
+
+<ng-container *ngIf="auxMeshes$ | async as auxMeshes">
+  <mat-divider class="mt-1 mb-1"></mat-divider>
+  <form [formGroup]="auxMeshFormGroup">
+    <mat-slide-toggle *ngFor="let auxMesh of auxMeshes; trackBy: trackByAtId"
+      [formControlName]="auxMesh['@id']"
+      class="d-block"
+      [name]="'toggle-aux-mesh-' + auxMesh['@id']">
+      <span class="iv-custom-comp text">
+        {{ auxMesh.displayName || auxMesh.name }}
+      </span>
+    </mat-slide-toggle>
+  </form>
+</ng-container>
+
diff --git a/src/viewerModule/threeSurfer/index.ts b/src/viewerModule/threeSurfer/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..748c74827ac5b56cf13833e1133129b42da3d85c
--- /dev/null
+++ b/src/viewerModule/threeSurfer/index.ts
@@ -0,0 +1 @@
+export { ThreeSurferModule } from './module'
diff --git a/src/viewerModule/threeSurfer/module.ts b/src/viewerModule/threeSurfer/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..626566404f2aa624d603184e40ee36895e80cedd
--- /dev/null
+++ b/src/viewerModule/threeSurfer/module.ts
@@ -0,0 +1,25 @@
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { FormsModule } from "@angular/forms";
+import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module";
+import { UtilModule } from "src/util";
+import { ThreeSurferGlueCmp } from "./threeSurferGlue/threeSurfer.component";
+import { ThreeSurferViewerConfig } from "./tsViewerConfig/tsViewerConfig.component";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    AngularMaterialModule,
+    UtilModule,
+    FormsModule,
+  ],
+  declarations: [
+    ThreeSurferGlueCmp,
+    ThreeSurferViewerConfig,
+  ],
+  exports: [
+    ThreeSurferGlueCmp,
+  ]
+})
+
+export class ThreeSurferModule{}
diff --git a/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.component.ts b/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c720fb1a073c5432924cc527d8192c5ca376806b
--- /dev/null
+++ b/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.component.ts
@@ -0,0 +1,637 @@
+import { Component, Input, Output, EventEmitter, ElementRef, OnChanges, OnDestroy, AfterViewInit, Inject, Optional } from "@angular/core";
+import { EnumViewerEvt, IViewer, TViewerEvent } from "src/viewerModule/viewer.interface";
+import { TThreeSurferConfig, TThreeSurferMode } from "../types";
+import { parseContext } from "../util";
+import { retry, flattenRegions } from 'common/util'
+import { Observable, Subject } from "rxjs";
+import { debounceTime, filter, switchMap } from "rxjs/operators";
+import { ComponentStore } from "src/viewerModule/componentStore";
+import { select, Store } from "@ngrx/store";
+import { viewerStateChangeNavigation, viewerStateSetSelectedRegions } from "src/services/state/viewerState/actions";
+import { viewerStateSelectorNavigation } from "src/services/state/viewerState/selectors";
+import { ClickInterceptor, CLICK_INTERCEPTOR_INJECTOR } from "src/util";
+import { REGION_OF_INTEREST } from "src/util/interfaces";
+import { MatSnackBar } from "@angular/material/snack-bar";
+import { CONST } from 'common/constants'
+import { API_SERVICE_SET_VIEWER_HANDLE_TOKEN, TSetViewerHandle } from "src/atlasViewer/atlasViewer.apiService.service";
+import { switchMapWaitFor } from "src/util/fn";
+
+const pZoomFactor = 5e3
+
+type THandlingCustomEv = {
+  regions: ({ name?: string, error?: string })[]
+  evMesh?: {
+    faceIndex: number
+    verticesIndicies: number[]
+  }
+}
+
+type TCameraOrientation = {
+  perspectiveOrientation: [number, number, number, number]
+  perspectiveZoom: number
+}
+
+const threshold = 1e-3
+
+function getHemisphereKey(region: { name: string }){
+  return /left/.test(region.name)
+    ? 'left'
+    : /right/.test(region.name)
+      ? 'right'
+      : null
+}
+
+function cameraNavsAreSimilar(c1: TCameraOrientation, c2: TCameraOrientation){
+  if (c1 === c2) return true
+  if (!!c1 && !!c2) return true
+
+  if (!c1 && !!c2) return false
+  if (!c2 && !!c1) return false
+
+  if (Math.abs(c1.perspectiveZoom - c2.perspectiveZoom) > threshold) return false
+  if ([0, 1, 2, 3].some(
+    idx => Math.abs(c1.perspectiveOrientation[idx] - c2.perspectiveOrientation[idx]) > threshold
+  )) {
+    return false
+  }
+  return true
+}
+
+@Component({
+  selector: 'three-surfer-glue-cmp',
+  templateUrl: './threeSurfer.template.html',
+  styleUrls: [
+    './threeSurfer.style.css'
+  ],
+  providers: [ ComponentStore ]
+})
+
+export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, OnChanges, AfterViewInit, OnDestroy {
+
+  private loanedColorMap = new WeakSet()
+
+  @Input()
+  selectedTemplate: any
+
+  @Input()
+  selectedParcellation: any
+  
+  @Output()
+  viewerEvent = new EventEmitter<TViewerEvent<'threeSurfer'>>()
+
+  private domEl: HTMLElement
+  private config: TThreeSurferConfig
+  public modes: TThreeSurferMode[] = []
+  public selectedMode: string
+
+  private mainStoreCameraNav: TCameraOrientation = null
+  private localCameraNav: TCameraOrientation = null
+
+  public allKeys: {name: string, checked: boolean}[] = []
+
+  private regionMap: Map<string, Map<number, any>> = new Map()
+  private mouseoverRegions = []
+  constructor(
+    private el: ElementRef,
+    private store$: Store<any>,
+    private navStateStoreRelay: ComponentStore<{ perspectiveOrientation: [number, number, number, number], perspectiveZoom: number }>,
+    private snackbar: MatSnackBar,
+    @Optional() @Inject(REGION_OF_INTEREST) private roi$: Observable<any>,
+    @Optional() @Inject(CLICK_INTERCEPTOR_INJECTOR) clickInterceptor: ClickInterceptor,
+    @Optional() @Inject(API_SERVICE_SET_VIEWER_HANDLE_TOKEN) setViewerHandle: TSetViewerHandle,
+  ){
+
+    // set viewer handle
+    // the API won't be 100% compatible with ngviewer
+    if (setViewerHandle) {
+      const nyi = () => {
+        throw new Error(`Not yet implemented`)
+      }
+      setViewerHandle({
+        add3DLandmarks: nyi,
+        loadLayer: nyi,
+        applyLayersColourMap: (map: Map<string, Map<number, { red: number, green: number, blue: number }>>) => {
+          if (this.loanedColorMap.has(map)) {
+            this.externalHemisphLblColorMap = null
+          } else {
+
+            const applyCm = new Map()
+            for (const [hem, m] of map.entries()) {
+              const nMap = new Map()
+              applyCm.set(hem, nMap)
+              for (const [lbl, vals] of m.entries()) {
+                const { red, green, blue } = vals
+                nMap.set(lbl, [red/255, green/255, blue/255])
+              }
+            }
+            this.externalHemisphLblColorMap = applyCm
+          }
+          this.applyColorMap()
+        },
+        getLayersSegmentColourMap: () => {
+          const map = this.getColormapCopy()
+          const outmap = new Map<string, Map<number, { red: number, green: number, blue: number }>>()
+          for (const [ hem, m ] of map.entries()) {
+            const nMap = new Map<number, {red: number, green: number, blue: number}>()
+            outmap.set(hem, nMap)
+            for (const [ lbl, vals ] of m.entries()) {
+              nMap.set(lbl, {
+                red: vals[0] * 255,
+                green: vals[1] * 255,
+                blue: vals[2] * 255,
+              })
+            }
+          }
+          this.loanedColorMap.add(outmap)
+          return outmap
+        },
+        getNgHash: nyi,
+        hideAllSegments: nyi,
+        hideSegment: nyi,
+        mouseEvent: null, 
+        mouseOverNehuba: null,
+        mouseOverNehubaLayers: null,
+        mouseOverNehubaUI: null,
+        moveToNavigationLoc: null,
+        moveToNavigationOri: null,
+        remove3DLandmarks: null,
+        removeLayer: null,
+        setLayerVisibility: null,
+        setNavigationLoc: null,
+        setNavigationOri: null,
+        showAllSegments: nyi,
+        showSegment: nyi,
+      })
+    }
+
+    this.onDestroyCb.push(
+      () => setViewerHandle(null)
+    )
+
+    if (this.roi$) {
+      const sub = this.roi$.pipe(
+        switchMap(switchMapWaitFor({
+          condition: () => this.colormapLoaded
+        }))
+      ).subscribe(r => {
+        try {
+          if (!r) throw new Error(`No region selected.`)
+          const cmap = this.getColormapCopy()
+          const hemisphere = getHemisphereKey(r)
+          if (!hemisphere) {
+            this.snackbar.open(CONST.CANNOT_DECIPHER_HEMISPHERE, 'Dismiss', {
+              duration: 3000
+            })
+            throw new Error(CONST.CANNOT_DECIPHER_HEMISPHERE)
+          }
+          for (const [ hem, m ] of cmap.entries()) {
+            for (const lbl of m.keys()) {
+              if (hem !== hemisphere || lbl !== r.labelIndex) {
+                m.set(lbl, [1, 1, 1])
+              }
+            }
+          }
+          this.internalHemisphLblColorMap = cmap
+        } catch (e) {
+          this.internalHemisphLblColorMap = null
+        }
+
+        this.applyColorMap()
+      })
+      this.onDestroyCb.push(
+        () => sub.unsubscribe()
+      )
+    }
+
+    /**
+     * intercept click and act
+     */
+    if (clickInterceptor) {
+      const handleClick = (ev: MouseEvent) => {
+
+        // if does not click inside container, ignore
+        if (!(this.el.nativeElement as HTMLElement).contains(ev.target as HTMLElement)) {
+          return true
+        }
+        
+        if (this.mouseoverRegions.length === 0) return true
+        if (this.mouseoverRegions.length > 1) {
+          this.snackbar.open(CONST.DOES_NOT_SUPPORT_MULTI_REGION_SELECTION, 'Dismiss', {
+            duration: 3000
+          })
+          return true
+        }
+        this.store$.dispatch(
+          viewerStateSetSelectedRegions({
+            selectRegions: this.mouseoverRegions
+          })
+        )
+        return true
+      }
+      const { register, deregister } = clickInterceptor
+      register(handleClick)
+      this.onDestroyCb.push(
+        () => deregister(register)
+      )
+    }
+    
+    this.domEl = this.el.nativeElement
+
+    /**
+     * subscribe to camera custom event
+     */
+    const cameraSub = this.cameraEv$.pipe(
+      filter(v => !!v),
+      debounceTime(160)
+    ).subscribe(() => {
+      
+      const THREE = (window as any).ThreeSurfer.THREE
+      
+      const q = new THREE.Quaternion()
+      const t = new THREE.Vector3()
+      const s = new THREE.Vector3()
+
+      const cameraM = this.tsRef.camera.matrix
+      cameraM.decompose(t, q, s)
+      try {
+        this.navStateStoreRelay.setState({
+          perspectiveOrientation: q.toArray(),
+          perspectiveZoom: t.length()
+        })
+      } catch (_e) {
+        // LockError, ignore
+      }
+    })
+
+    this.onDestroyCb.push(
+      () => cameraSub.unsubscribe()
+    )
+
+    /**
+     * subscribe to navstore relay store and negotiate setting global state
+     */
+    const navStateSub = this.navStateStoreRelay.select(s => s).subscribe(v => {
+      this.store$.dispatch(
+        viewerStateChangeNavigation({
+          navigation: {
+            position: [0, 0, 0],
+            orientation: [0, 0, 0, 1],
+            zoom: 1,
+            perspectiveOrientation: v.perspectiveOrientation,
+            perspectiveZoom: v.perspectiveZoom * pZoomFactor
+          }
+        })
+      )
+    })
+
+    this.onDestroyCb.push(
+      () => navStateSub.unsubscribe()
+    )
+
+    /**
+     * subscribe to main store and negotiate with relay to set camera
+     */
+    const navSub = this.store$.pipe(
+      select(viewerStateSelectorNavigation)
+    ).subscribe(nav => {
+      const { perspectiveOrientation, perspectiveZoom } = nav
+      this.mainStoreCameraNav = {
+        perspectiveOrientation,
+        perspectiveZoom
+      }
+
+      if (!cameraNavsAreSimilar(this.mainStoreCameraNav, this.localCameraNav)) {
+        this.relayStoreLock = this.navStateStoreRelay.getLock()
+        const THREE = (window as any).ThreeSurfer.THREE
+        
+        const cameraQuat = new THREE.Quaternion(...this.mainStoreCameraNav.perspectiveOrientation)
+        const cameraPos = new THREE.Vector3(0, 0, this.mainStoreCameraNav.perspectiveZoom / pZoomFactor)
+        cameraPos.applyQuaternion(cameraQuat)
+        this.toTsRef(tsRef => {
+          tsRef.camera.position.copy(cameraPos)
+          if (this.relayStoreLock) this.relayStoreLock()
+        })
+      }
+    })
+
+    this.onDestroyCb.push(
+      () => navSub.unsubscribe()
+    )
+  }
+
+  tsRef: any
+  loadedMeshes: {
+    threeSurfer: any
+    mesh: string
+    colormap: string
+    hemisphere: string
+    vIdxArr: number[]
+  }[] = []
+  private hemisphLblColorMap: Map<string, Map<number, [number, number, number]>> = new Map()
+  private internalHemisphLblColorMap: Map<string, Map<number, [number, number, number]>>
+  private externalHemisphLblColorMap: Map<string, Map<number, [number, number, number]>>
+  
+  get activeColorMap() {
+    if (this.externalHemisphLblColorMap) return this.externalHemisphLblColorMap
+    if (this.internalHemisphLblColorMap) return this.internalHemisphLblColorMap
+    return this.hemisphLblColorMap
+  }
+  private relayStoreLock: () => void = null
+  private tsRefInitCb: ((tsRef: any) => void)[] = []
+  private toTsRef(callback: (tsRef: any) => void) {
+    if (this.tsRef) {
+      callback(this.tsRef)
+      return
+    }
+    this.tsRefInitCb.push(callback)
+  }
+
+  private unloadAllMeshes() {
+    this.allKeys = []
+    while(this.loadedMeshes.length > 0) {
+      const m = this.loadedMeshes.pop()
+      this.tsRef.unloadMesh(m.threeSurfer)
+    }
+    this.hemisphLblColorMap.clear()
+    this.colormapLoaded = false
+  }
+
+  public async loadMode(mode: TThreeSurferMode) {
+    
+    this.unloadAllMeshes()
+
+    this.selectedMode = mode.name
+    const { meshes } = mode
+    await retry(async () => {
+      for (const singleMesh of meshes) {
+        const { hemisphere } = singleMesh
+        if (!this.regionMap.has(hemisphere)) throw new Error(`regionmap does not have hemisphere defined!`)
+      }
+    }, {
+      timeout: 32,
+      retries: 10
+    })
+    for (const singleMesh of meshes) {
+      const { mesh, colormap, hemisphere } = singleMesh
+      this.allKeys.push({name: hemisphere, checked: true})
+
+      const tsM = await this.tsRef.loadMesh(
+        parseContext(mesh, [this.config['@context']])
+      )
+
+      if (!this.regionMap.has(hemisphere)) continue
+      const rMap = this.regionMap.get(hemisphere)
+      const applyCM = new Map()
+      for (const [ lblIdx, region ] of rMap.entries()) {
+        applyCM.set(lblIdx, (region.rgb || [200, 200, 200]).map(v => v/255))
+      }
+
+      const tsC = await this.tsRef.loadColormap(
+        parseContext(colormap, [this.config['@context']])
+      )
+      
+      let colorIdx = tsC[0].getData()
+      if (tsC[0].attributes.DataType === 'NIFTI_TYPE_INT16') {
+        colorIdx = (window as any).ThreeSurfer.GiftiBase.castF32UInt16(colorIdx)
+      }
+
+      this.loadedMeshes.push({
+        threeSurfer: tsM,
+        colormap,
+        mesh,
+        hemisphere,
+        vIdxArr: colorIdx
+      })
+
+      this.hemisphLblColorMap.set(hemisphere, applyCM)
+    }
+    this.colormapLoaded = true
+    this.applyColorMap()
+  }
+
+  private colormapLoaded = false
+
+  private getColormapCopy(): Map<string, Map<number, [number, number, number]>> {
+    const outmap = new Map()
+    for (const [key, value] of this.hemisphLblColorMap.entries()) {
+      outmap.set(key, new Map(value))
+    }
+    return outmap
+  }
+
+  /**
+   * TODO perhaps debounce calls to applycolormap
+   * so that the colormap doesn't "flick"
+   */
+  private applyColorMap(){
+    /**
+     * on apply color map, reset mesh visibility
+     * this issue is more difficult to solve than first anticiplated.
+     * test scenarios:
+     * 
+     * 1/ hide hemisphere, select region
+     * 2/ hide hemisphere, select region, unhide hemisphere
+     * 3/ select region, hide hemisphere, deselect region
+     */
+    for (const key of this.allKeys) {
+      key.checked = true
+    }
+    for (const mesh of this.loadedMeshes) {
+      const { hemisphere, threeSurfer, vIdxArr } = mesh
+      const applyCM = this.activeColorMap.get(hemisphere)
+      this.tsRef.applyColorMap(threeSurfer, vIdxArr, 
+        {
+          custom: applyCM
+        }
+      )
+    }
+  }
+
+  async ngOnChanges(){
+    if (this.tsRef) {
+      this.ngOnDestroy()
+      this.ngAfterViewInit()
+    }
+    if (this.selectedTemplate) {
+
+      /**
+       * wait until threesurfer is defined in window
+       */
+      await retry(async () => {
+        if (typeof (window as any).ThreeSurfer === 'undefined') throw new Error('ThreeSurfer not yet defined')
+      }, {
+        timeout: 160,
+        retries: 10,
+      })
+      
+      this.config = this.selectedTemplate['three-surfer']
+      // somehow curv ... cannot be parsed properly by gifti parser... something about points missing
+      this.modes = this.config.modes.filter(m => !/curv/.test(m.name))
+      if (!this.tsRef) {
+        this.tsRef = new (window as any).ThreeSurfer(this.domEl, {highlightHovered: true})
+        this.onDestroyCb.push(
+          () => {
+            this.tsRef.dispose()
+            this.tsRef = null
+          }
+        );
+        this.tsRef.control.enablePan = false
+        while (this.tsRefInitCb.length > 0) this.tsRefInitCb.pop()(this.tsRef)
+      }
+
+      const flattenedRegions = flattenRegions(this.selectedParcellation.regions)
+      for (const region of flattenedRegions) {
+        if (region.labelIndex) {
+          const hemisphere = getHemisphereKey(region)
+          if (!hemisphere) throw new Error(`region ${region.name} does not have hemisphere defined`)
+          if (!this.regionMap.has(hemisphere)) {
+            this.regionMap.set(hemisphere, new Map())
+          }
+          const rMap = this.regionMap.get(hemisphere)
+          rMap.set(region.labelIndex, region)
+        }
+      }
+      
+      // load mode0 by default
+      this.loadMode(this.config.modes[0])
+
+      this.viewerEvent.emit({
+        type: EnumViewerEvt.VIEWERLOADED,
+        data: true
+      })
+    }
+  }
+
+  private handleCustomMouseEv(detail: any){
+    const evMesh = detail.mesh && {
+      faceIndex: detail.mesh.faceIndex,
+      // typo in three-surfer
+      verticesIndicies: detail.mesh.verticesIdicies
+    }
+    const custEv: THandlingCustomEv = {
+      regions: [],
+      evMesh
+    }
+    
+    if (!detail.mesh) {
+      return this.handleMouseoverEvent(custEv)
+    }
+
+    const evGeom = detail.mesh.geometry
+    const evVertIdx = detail.mesh.verticesIdicies
+    const found = this.loadedMeshes.find(({ threeSurfer }) => threeSurfer === evGeom)
+    if (!found) return this.handleMouseoverEvent(custEv)
+
+    /**
+     * check if the mesh is toggled off
+     * if so, do not proceed
+     */
+    const checkKey = this.allKeys.find(key => key.name === found.hemisphere)
+    if (checkKey && !checkKey.checked) return
+
+    const { hemisphere: key, vIdxArr } = found
+
+    if (!key || !evVertIdx) {
+      return this.handleMouseoverEvent(custEv)
+    }
+
+    const labelIdxSet = new Set<number>()
+    
+    for (const vIdx of evVertIdx) {
+      labelIdxSet.add(
+        vIdxArr[vIdx]
+      )
+    }
+    if (labelIdxSet.size === 0) {
+      return this.handleMouseoverEvent(custEv)
+    }
+
+    const hemisphereMap = this.regionMap.get(key)
+
+    if (!hemisphereMap) {
+      custEv.regions = Array.from(labelIdxSet).map(v => {
+        return {
+          error: `unknown#${v}`
+        }
+      })
+      return this.handleMouseoverEvent(custEv)
+    }
+
+    custEv.regions =  Array.from(labelIdxSet)
+      .map(lblIdx => {
+        const ontoR = hemisphereMap.get(lblIdx)
+        if (ontoR) {
+          return ontoR
+        } else {
+          return {
+            error: `unkonwn#${lblIdx}`
+          }
+        }
+      })
+    return this.handleMouseoverEvent(custEv)
+
+  }
+
+  private cameraEv$ = new Subject<{ position: { x: number, y: number, z: number }, zoom: number }>()
+  private handleCustomCameraEvent(detail: any){
+    this.cameraEv$.next(detail)
+  }
+
+  ngAfterViewInit(){
+    const customEvHandler = (ev: CustomEvent) => {
+      const { type, data } = ev.detail
+      if (type === 'mouseover') {
+        return this.handleCustomMouseEv(data)
+      }
+      if (type === 'camera') {
+        return this.handleCustomCameraEvent(data)
+      }
+    }
+    this.domEl.addEventListener((window as any).ThreeSurfer.CUSTOM_EVENTNAME_UPDATED, customEvHandler)
+    this.onDestroyCb.push(
+      () => this.domEl.removeEventListener((window as any).ThreeSurfer.CUSTOM_EVENTNAME_UPDATED, customEvHandler)
+    )
+  }
+
+  public mouseoverText: string
+  private handleMouseoverEvent(ev: THandlingCustomEv){
+    const { regions: mouseover, evMesh } = ev
+    this.mouseoverRegions = mouseover
+    this.viewerEvent.emit({
+      type: EnumViewerEvt.VIEWER_CTX,
+      data: {
+        viewerType: 'threeSurfer',
+        payload: {
+          fsversion: this.selectedMode,
+          faceIndex: evMesh?.faceIndex,
+          vertexIndices: evMesh?.verticesIndicies,
+          position: [],
+          _mouseoverRegion: mouseover.filter(el => !el.error)
+        }
+      }
+    })
+    this.mouseoverText = mouseover.length === 0 ?
+      null :
+      mouseover.map(
+        el => el.name || el.error
+      ).join(' / ')
+  }
+
+  public handleCheckBox(key: { name: string, checked: boolean }, flag: boolean){
+    const foundMesh = this.loadedMeshes.find(m => m.hemisphere === key.name)
+    if (!foundMesh) {
+      throw new Error(`Cannot find mesh with name: ${key.name}`)
+    }
+    const meshObj = this.tsRef.customColormap.get(foundMesh.threeSurfer)
+    if (!meshObj) {
+      throw new Error(`mesh obj not found!`)
+    }
+    meshObj.mesh.visible = flag
+  }
+
+  private onDestroyCb: (() => void) [] = []
+
+  ngOnDestroy() {
+    while (this.onDestroyCb.length > 0) this.onDestroyCb.pop()()
+  }
+}
diff --git a/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.style.css b/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..c5538b29efd15dbcca152e91581581b6525e90f2
--- /dev/null
+++ b/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.style.css
@@ -0,0 +1,28 @@
+:host
+{
+  display: block;
+  height: 100%;
+  width: 100%;
+}
+
+.button-container
+{
+  z-index: 1;
+  position: fixed;
+  bottom: 1rem;
+  right: 1rem;
+  width: 0px;
+  height:0px;
+
+  display: flex;
+  justify-content: flex-end;
+  align-items: flex-end;
+}
+
+span.mouseover
+{
+  z-index: 1;
+  position: fixed;
+  bottom: 3rem;
+  right: 1rem;
+}
diff --git a/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.template.html b/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..56a25de0502ffc08a17a2a560cc83be60d7e7bcc
--- /dev/null
+++ b/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.template.html
@@ -0,0 +1,43 @@
+<span *ngIf="mouseoverText"
+  class="mouseover iv-custom-comp text">
+  {{ mouseoverText }}
+</span>
+
+<div class="button-container">
+
+  <!-- selector & configurator -->
+  <button mat-icon-button
+    color="primary"
+    class="pe-all"
+    [matMenuTriggerFor]="fsModeSelMenu">
+    <i class="fas fa-bars"></i>
+  </button>
+</div>
+
+
+<!-- selector/configurator menu -->
+<mat-menu #fsModeSelMenu="matMenu">
+
+  <div class="iv-custom-comp text pl-2 m-2">
+    <mat-checkbox *ngFor="let key of allKeys"
+      class="d-block"
+      iav-stop="click"
+      (ngModelChange)="handleCheckBox(key, $event)"
+      [(ngModel)]="key.checked">
+      {{ key.name }}
+    </mat-checkbox>
+  </div>
+  <mat-divider></mat-divider>
+  <button *ngFor="let mode of modes"
+    mat-menu-item
+    (click)="loadMode(mode)"
+    color="primary">
+    <mat-icon
+      fontSet="fas"
+      [fontIcon]="mode.name === selectedMode ? 'fa-circle' : 'fa-none'">
+    </mat-icon>
+    <span>
+      {{ mode.name }}
+    </span>
+  </button>
+</mat-menu>
diff --git a/src/viewerModule/threeSurfer/tsViewerConfig/tsViewerConfig.component.ts b/src/viewerModule/threeSurfer/tsViewerConfig/tsViewerConfig.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d59b43509047e1853dca0e9be80c6753f8ba65b9
--- /dev/null
+++ b/src/viewerModule/threeSurfer/tsViewerConfig/tsViewerConfig.component.ts
@@ -0,0 +1,13 @@
+import { Component } from "@angular/core";
+
+@Component({
+  selector: 'three-surfer-viewer-config',
+  templateUrl: './tsViewerConfig.template.html',
+  styleUrls: [
+    './tsViewerConfig.style.css'
+  ]
+})
+
+export class ThreeSurferViewerConfig {
+  
+}
diff --git a/src/viewerModule/threeSurfer/tsViewerConfig/tsViewerConfig.style.css b/src/viewerModule/threeSurfer/tsViewerConfig/tsViewerConfig.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/viewerModule/threeSurfer/tsViewerConfig/tsViewerConfig.template.html b/src/viewerModule/threeSurfer/tsViewerConfig/tsViewerConfig.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..578678e805ef42854e97e7d420f8146f584d0af5
--- /dev/null
+++ b/src/viewerModule/threeSurfer/tsViewerConfig/tsViewerConfig.template.html
@@ -0,0 +1 @@
+tsviewer config
diff --git a/src/viewerModule/threeSurfer/types.ts b/src/viewerModule/threeSurfer/types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d6f987870b2f1f6a628f69a0742c8e4177b4b38a
--- /dev/null
+++ b/src/viewerModule/threeSurfer/types.ts
@@ -0,0 +1,25 @@
+import { IContext } from './util'
+
+export type TThreeSurferMesh = {
+  colormap: string
+  mesh: string
+  hemisphere: 'left' | 'right'
+}
+
+export type TThreeSurferMode = {
+  name: string
+  meshes: TThreeSurferMesh[]
+}
+
+export type TThreeSurferConfig = {
+  ['@context']: IContext
+  modes: TThreeSurferMode[]
+}
+
+export type TThreeSurferContextInfo = {
+  position: number[]
+  faceIndex: number
+  vertexIndices: number[]
+  fsversion: string
+  _mouseoverRegion?: any[]
+}
diff --git a/src/viewerModule/threeSurfer/util.ts b/src/viewerModule/threeSurfer/util.ts
new file mode 100644
index 0000000000000000000000000000000000000000..34a0d85065cf7104d57faf08790dd943804f7b26
--- /dev/null
+++ b/src/viewerModule/threeSurfer/util.ts
@@ -0,0 +1,14 @@
+export interface IContext {
+  [key: string]: string
+}
+
+export function parseContext(input: string, contexts: IContext[]){
+  let output = input
+  for (const context of contexts) {
+    for (const key in context) {
+      const re = new RegExp(`${key}:`, 'g')
+      output = output.replace(re, context[key])
+    }
+  }
+  return output
+}
diff --git a/src/viewerModule/viewer.interface.ts b/src/viewerModule/viewer.interface.ts
new file mode 100644
index 0000000000000000000000000000000000000000..195ee958adbbeb7af67ff27000745d0cc8a67376
--- /dev/null
+++ b/src/viewerModule/viewer.interface.ts
@@ -0,0 +1,71 @@
+import { EventEmitter } from "@angular/core";
+import { TNehubaContextInfo } from "./nehuba/types";
+import { TThreeSurferContextInfo } from "./threeSurfer/types";
+
+type TLayersColorMap = Map<string, Map<number, { red: number, green: number, blue: number }>>
+
+interface IViewerCtrl {
+
+  // navigation control
+  setNavigationLoc(coord: number[], realSpace?: boolean): void
+  moveToNavigationLoc(coord: number[], realSpace?: boolean): void
+  setNavigationOri(quat: number[]): void
+  moveToNavigationOri(quat: number[]): void
+
+  // segment control
+  showSegment(segment: any): void
+  hideSegment(segment: any): void
+  showAllSegments(): void
+  hideAllSegments(): void
+
+  // landmark control
+  addLandmarks(landmarks: any[]): void
+  removeLandmarks(landmarks: any[]): void
+
+  // layer control
+  addLayer(layerSpec: any): void
+  removeLayer(layerId: string): void
+  applyLayersColourMap(map: TLayersColorMap): void
+  getLayersColourMap(): TLayersColorMap
+}
+
+export interface IViewerCtx {
+  'nehuba': TNehubaContextInfo
+  'threeSurfer': TThreeSurferContextInfo
+}
+
+export type TContextArg<K extends keyof IViewerCtx> = ({
+  viewerType: K
+  payload: IViewerCtx[K]
+})
+
+export enum EnumViewerEvt {
+  VIEWERLOADED,
+  VIEWER_CTX,
+}
+
+type TViewerEventViewerLoaded = {
+  type: EnumViewerEvt.VIEWERLOADED
+  data: boolean
+}
+
+export type TViewerEvent<T extends keyof IViewerCtx> = TViewerEventViewerLoaded |
+  {
+    type: EnumViewerEvt.VIEWER_CTX
+    data: TContextArg<T>
+  }
+
+export type TSupportedViewers = keyof IViewerCtx
+
+export interface IViewer<K extends keyof IViewerCtx> {
+  
+  selectedTemplate: any
+  selectedParcellation: any
+  viewerCtrlHandler?: IViewerCtrl
+  viewerEvent: EventEmitter<TViewerEvent<K>>
+}
+
+export interface IGetContextInjArg {
+  register: (fn: (contextArg: TContextArg<TSupportedViewers>) => void) => void
+  deregister: (fn: (contextArg: TContextArg<TSupportedViewers>) => void) => void
+}
diff --git a/src/viewerModule/viewerCmp/viewerCmp.component.spec.ts b/src/viewerModule/viewerCmp/viewerCmp.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..dba48dfcf41e1ed1a47ac8fc1502663c0026c81c
--- /dev/null
+++ b/src/viewerModule/viewerCmp/viewerCmp.component.spec.ts
@@ -0,0 +1,125 @@
+import { TestBed } from "@angular/core/testing"
+import { MockStore, provideMockStore } from "@ngrx/store/testing"
+import { hot } from "jasmine-marbles"
+import { Observable, of, throwError } from "rxjs"
+import { viewerStateContextedSelectedRegionsSelector } from "src/services/state/viewerState/selectors"
+import { ROIFactory } from "./viewerCmp.component"
+
+describe('> viewerCmp.component.ts', () => {
+  let mockStore: MockStore
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      providers: [
+        provideMockStore()
+      ]
+    })
+    mockStore = TestBed.inject(MockStore)
+  })
+  describe('> ROIFactory', () => {
+    const mockDetail = {
+      foo: 'bar'
+    }
+    class MockPCSvc {
+      getRegionDetail(){
+        return of(mockDetail)
+      }
+    }
+    const pcsvc = new MockPCSvc()
+    let getRegionDetailSpy:  jasmine.Spy
+
+    beforeEach(() => {
+      getRegionDetailSpy = spyOn(pcsvc, 'getRegionDetail')
+      mockStore.overrideSelector(viewerStateContextedSelectedRegionsSelector, [])
+    })
+    
+    afterEach(() => {
+      getRegionDetailSpy.calls.reset()
+    })
+
+    describe('> if regoinselected is empty array', () => {
+      let returnVal: Observable<any>
+      beforeEach(() => {
+        getRegionDetailSpy.and.callThrough()
+        returnVal = ROIFactory(mockStore, pcsvc as any)
+      })
+      it('> returns null', () => {
+        expect(
+          returnVal
+        ).toBeObservable(hot('a', {
+          a: null
+        }))
+      })
+
+      it('> regionDetail not called', () => {
+        expect(getRegionDetailSpy).not.toHaveBeenCalled()
+      })
+    })
+
+    describe('> if regionselected is nonempty', () => {
+      const mockRegion = {
+        context: {
+          template: {
+            '@id': 'template-id'
+          },
+          parcellation: {
+            '@id': 'parcellation-id'
+          },
+          atlas: {
+            '@id': 'atlas-id'
+          }
+        },
+        ngId: 'foo-bar',
+        labelIndex: 123
+      }
+      const returnDetail = {
+        map: {
+          hello: 'world'
+        }
+      }
+      let returnVal: Observable<any>
+      beforeEach(() => {
+        getRegionDetailSpy.and.callFake(() => of(returnDetail))
+        mockStore.overrideSelector(viewerStateContextedSelectedRegionsSelector, [mockRegion])
+        returnVal = ROIFactory(mockStore, pcsvc as any)
+      })
+
+      // TODO check why marble is acting weird
+      // and that null is not emitted
+      it('> returns as expected', () => {
+        expect(returnVal).toBeObservable(
+          hot('b', {
+            a: null,
+            b: {
+              ...mockRegion,
+              ...returnDetail
+            }
+          })
+        )
+        const { context } = mockRegion
+        expect(getRegionDetailSpy).toHaveBeenCalledWith(
+          context.atlas["@id"],
+          context.parcellation["@id"],
+          context.template["@id"],
+          mockRegion
+        )
+      })
+
+      it('> if getRegionDetail throws, at least return original region', () => {
+        getRegionDetailSpy.and.callFake(() => throwError('blabla'))
+        expect(returnVal).toBeObservable(
+          hot('b', {
+            a: null,
+            b: mockRegion
+          })
+        )
+        const { context } = mockRegion
+        expect(getRegionDetailSpy).toHaveBeenCalledWith(
+          context.atlas["@id"],
+          context.parcellation["@id"],
+          context.template["@id"],
+          mockRegion
+        )
+      })
+    })
+  })
+})
diff --git a/src/viewerModule/viewerCmp/viewerCmp.component.ts b/src/viewerModule/viewerCmp/viewerCmp.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f0eb2af8c0f4b65ef971973b6b979ab7a36b203c
--- /dev/null
+++ b/src/viewerModule/viewerCmp/viewerCmp.component.ts
@@ -0,0 +1,400 @@
+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 {catchError, distinctUntilChanged, filter, map, shareReplay, startWith, switchMap } from "rxjs/operators";
+import { viewerStateSetSelectedRegions } from "src/services/state/viewerState/actions";
+import {
+  viewerStateContextedSelectedRegionsSelector,
+  viewerStateSelectedParcellationSelector,
+  viewerStateSelectedTemplateSelector,
+  viewerStateStandAloneVolumes,
+  viewerStateViewerModeSelector
+} from "src/services/state/viewerState/selectors"
+import { CONST, ARIA_LABELS, QUICKTOUR_DESC } from 'common/constants'
+import { OVERWRITE_SHOW_DATASET_DIALOG_TOKEN, REGION_OF_INTEREST } from "src/util/interfaces";
+import { animate, state, style, transition, trigger } from "@angular/animations";
+import { SwitchDirective } from "src/util/directives/switch.directive";
+import { QuickTourThis, IQuickTourData } from "src/ui/quickTour";
+import { MatDrawer } from "@angular/material/sidenav";
+import { PureContantService } from "src/util";
+import { EnumViewerEvt, TContextArg, TSupportedViewers, TViewerEvent } from "../viewer.interface";
+import { getGetRegionFromLabelIndexId, switchMapWaitFor } from "src/util/fn";
+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";
+
+type TCStoreViewerCmp = {
+  overlaySideNav: any
+}
+
+export function ROIFactory(store: Store<any>, svc: PureContantService){
+  return store.pipe(
+    select(viewerStateContextedSelectedRegionsSelector),
+    switchMap(r => {
+      if (!r[0]) return of(null)
+      const { context } = r[0]
+      const { atlas, template, parcellation } = context || {}
+      return merge(
+        of(null),
+        svc.getRegionDetail(atlas['@id'], parcellation['@id'], template['@id'], r[0]).pipe(
+          map(det => {
+            return {
+              ...r[0],
+              ...det,
+            }
+          }),
+          // in case detailed requests fails
+          catchError((_err, _obs) => of(r[0])),
+        )
+      )
+    }),
+    shareReplay(1)
+  )
+}
+
+@Component({
+  selector: 'iav-cmp-viewer-container',
+  templateUrl: './viewerCmp.template.html',
+  styleUrls: [
+    './viewerCmp.style.css'
+  ],
+  exportAs: 'iavCmpViewerCntr',
+  animations: [
+    trigger('openClose', [
+      state('open', style({
+        transform: 'translateY(0)',
+        opacity: 1
+      })),
+      state('closed', style({
+        transform: 'translateY(-100vh)',
+        opacity: 0
+      })),
+      transition('open => closed', [
+        animate('200ms cubic-bezier(0.35, 0, 0.25, 1)')
+      ]),
+      transition('closed => open', [
+        animate('200ms cubic-bezier(0.35, 0, 0.25, 1)')
+      ])
+    ]),
+    trigger('openCloseAnchor', [
+      state('open', style({
+        transform: 'translateY(0)'
+      })),
+      state('closed', style({
+        transform: 'translateY(100vh)'
+      })),
+      transition('open => closed', [
+        animate('200ms cubic-bezier(0.35, 0, 0.25, 1)')
+      ]),
+      transition('closed => open', [
+        animate('200ms cubic-bezier(0.35, 0, 0.25, 1)')
+      ])
+    ]),
+  ],
+  providers: [
+    {
+      provide: REGION_OF_INTEREST,
+      useFactory: ROIFactory,
+      deps: [ Store, PureContantService ]
+    },
+    {
+      provide: OVERWRITE_SHOW_DATASET_DIALOG_TOKEN,
+      useFactory: (cStore: ComponentStore<TCStoreViewerCmp>) => {
+        return function overwriteShowDatasetDialog( arg: any ){
+          
+          cStore.setState({
+            overlaySideNav: arg
+          })
+        }
+      },
+      deps: [ ComponentStore ]
+    },
+    ComponentStore
+  ]
+})
+
+export class ViewerCmp implements OnDestroy {
+
+  public CONST = CONST
+  public ARIA_LABELS = ARIA_LABELS
+
+  @ViewChild('sideNavTopSwitch', { static: true })
+  private sidenavTopSwitch: SwitchDirective
+
+  @ViewChild('sideNavFullLeftSwitch', { static: true })
+  private sidenavLeftSwitch: SwitchDirective
+
+  @ViewChild('genericInfoVCR', { read: ViewContainerRef })
+  genericInfoVCR: ViewContainerRef
+
+  public quickTourRegionSearch: IQuickTourData = {
+    order: 7,
+    description: QUICKTOUR_DESC.REGION_SEARCH,
+  }
+  public quickTourAtlasSelector: IQuickTourData = {
+    order: 0,
+    description: QUICKTOUR_DESC.ATLAS_SELECTOR,
+  }
+
+  @Input() ismobile = false
+
+  private subscriptions: Subscription[] = []
+  private onDestroyCb: (() => void)[]  = []
+  public viewerLoaded: boolean = false
+
+  public templateSelected$ = this.store$.pipe(
+    select(viewerStateSelectedTemplateSelector),
+    distinctUntilChanged(),
+  )
+  public parcellationSelected$ = this.store$.pipe(
+    select(viewerStateSelectedParcellationSelector),
+    distinctUntilChanged(),
+  )
+
+  public selectedRegions$ = this.store$.pipe(
+    select(viewerStateContextedSelectedRegionsSelector),
+    distinctUntilChanged(),
+  )
+
+  public isStandaloneVolumes$ = this.store$.pipe(
+    select(viewerStateStandAloneVolumes),
+    map(v => v.length > 0)
+  )
+
+  public viewerMode$: Observable<string> = this.store$.pipe(
+    select(viewerStateViewerModeSelector),
+  )
+
+  public overlaySidenav$ = this.cStore.select(s => s.overlaySideNav).pipe(
+    shareReplay(1),
+  )
+
+  public useViewer$: Observable<TSupportedViewers | 'notsupported'> = combineLatest([
+    this.templateSelected$,
+    this.isStandaloneVolumes$,
+  ]).pipe(
+    map(([t, isSv]) => {
+      if (isSv) return 'nehuba'
+      if (!t) return null
+      if (!!t['nehubaConfigURL'] || !!t['nehubaConfig']) return 'nehuba'
+      if (!!t['three-surfer']) return 'threeSurfer'
+      return 'notsupported'
+    })
+  )
+
+  /**
+   * TODO may need to be deprecated
+   * in favour of regional feature/data feature
+   */
+  public iavAdditionalLayers$ = new Subject<any[]>()
+
+  /**
+   * if no regions are selected, nor any additional layers (being deprecated)
+   * then the "explore" btn should not show
+   * and the full left side bar should not be expandable
+   * if it is already expanded, it should collapse
+   */
+  public alwaysHideMinorPanel$: Observable<boolean> = combineLatest([
+    this.selectedRegions$,
+    this.iavAdditionalLayers$.pipe(
+      startWith([])
+    )
+  ]).pipe(
+    map(([ regions, layers ]) => regions.length === 0 && layers.length === 0)
+  )
+
+  @ViewChild('viewerStatusCtxMenu', { read: TemplateRef })
+  private viewerStatusCtxMenu: TemplateRef<any>
+
+  @ViewChild('viewerStatusRegionCtxMenu', { read: TemplateRef })
+  private viewerStatusRegionCtxMenu: TemplateRef<any>
+
+  public context: TContextArg<TSupportedViewers>
+  private templateSelected: any
+  private getRegionFromlabelIndexId: Function
+
+  private genericInfoCF: ComponentFactory<GenericInfoCmp>
+  constructor(
+    private store$: Store<any>,
+    private viewerModuleSvc: ContextMenuService<TContextArg<'threeSurfer' | 'nehuba'>>,
+    private cStore: ComponentStore<TCStoreViewerCmp>,
+    cfr: ComponentFactoryResolver,
+    @Optional() @Inject(REGION_OF_INTEREST) public regionOfInterest$: Observable<any>
+  ){
+
+    this.genericInfoCF = cfr.resolveComponentFactory(GenericInfoCmp)
+
+    this.subscriptions.push(
+      this.selectedRegions$.subscribe(() => {
+        this.clearPreviewingDataset()
+      }),
+      this.alwaysHideMinorPanel$.pipe(
+        distinctUntilChanged(),
+        filter(flag => !flag),
+      ).subscribe(() => {
+        this.openSideNavs()
+      }),
+      this.viewerModuleSvc.context$.subscribe(
+        (ctx: any) => this.context = ctx
+      ),
+      this.templateSelected$.subscribe(
+        t => this.templateSelected = t
+      ),
+      this.parcellationSelected$.subscribe(
+        p => {
+          this.getRegionFromlabelIndexId = !!p
+            ? getGetRegionFromLabelIndexId({ parcellation: p })
+            : null
+        }
+      )
+    )
+  }
+
+  ngAfterViewInit(){
+    const cb: TContextMenuReg<TContextArg<'nehuba' | 'threeSurfer'>> = ({ append, context }) => {
+
+      /**
+       * first append general viewer info
+       */
+      append({
+        tmpl: this.viewerStatusCtxMenu,
+        data: {
+          context,
+          metadata: {
+            template: this.templateSelected,
+          }
+        },
+        order: 0
+      })
+
+      /**
+       * check hovered region
+       */
+      let hoveredRegions = []
+      if (context.viewerType === 'nehuba') {
+        hoveredRegions = (context as TContextArg<'nehuba'>).payload.nehuba.reduce(
+          (acc, curr) => acc.concat(
+            curr.labelIndices.map(
+              lblIdx => {
+                const labelIndexId = `${curr.layerName}#${lblIdx}`
+                if (!!this.getRegionFromlabelIndexId) {
+                  return this.getRegionFromlabelIndexId({
+                    labelIndexId: `${curr.layerName}#${lblIdx}`
+                  })
+                }
+                return labelIndexId
+              }
+            )
+          ),
+          []
+        )
+      }
+
+      if (context.viewerType === 'threeSurfer') {
+        hoveredRegions = (context as TContextArg<'threeSurfer'>).payload._mouseoverRegion
+      }
+
+      if (hoveredRegions.length > 0) {
+        append({
+          tmpl: this.viewerStatusRegionCtxMenu,
+          data: {
+            context,
+            metadata: { hoveredRegions }
+          },
+          order: 5
+        })
+      }
+
+      return true
+    }
+    this.viewerModuleSvc.register(cb)
+    this.onDestroyCb.push(
+      () => this.viewerModuleSvc.deregister(cb)
+    )
+    this.subscriptions.push(
+      this.overlaySidenav$.pipe(
+        switchMap(switchMapWaitFor({
+          condition: () => !!this.genericInfoVCR
+        }))
+      ).subscribe(data => {
+        if (!this.genericInfoVCR) {
+          console.warn(`genericInfoVCR not defined!`)
+          return
+        }
+        const injector = Injector.create({
+          providers: [{
+            provide: MAT_DIALOG_DATA,
+            useValue: data
+          }]
+        })
+
+        this.genericInfoVCR.clear()
+        this.genericInfoVCR.createComponent(this.genericInfoCF, null, injector)
+
+      })
+    )
+  }
+
+  ngOnDestroy() {
+    while (this.subscriptions.length) this.subscriptions.pop().unsubscribe()
+    while (this.onDestroyCb.length > 0) this.onDestroyCb.pop()()
+  }
+
+  public selectRoi(roi: any) {
+    this.store$.dispatch(
+      viewerStateSetSelectedRegions({
+        selectRegions: [ roi ]
+      })
+    )
+  }
+
+  public handleChipClick(){
+    this.openSideNavs()
+  }
+
+  private openSideNavs() {
+    this.sidenavLeftSwitch && this.sidenavLeftSwitch.open()
+    this.sidenavTopSwitch && this.sidenavTopSwitch.open()
+  }
+
+  public clearPreviewingDataset(){
+    /**
+     * clear all preview
+     */
+    this.cStore.setState({
+      overlaySideNav: null
+    })
+  }
+
+  @ViewChild('regionSelRef', { read: ElementRef })
+  regionSelRef: ElementRef<any>
+
+  @ViewChild('regionSearchQuickTour', { read: QuickTourThis })
+  regionSearchQuickTour: QuickTourThis
+
+  @ViewChild('matDrawerLeft', { read: MatDrawer })
+  matDrawerLeft: MatDrawer
+
+  handleSideNavAnimationDone(sideNavExpanded: boolean) {
+    this.regionSearchQuickTour?.attachTo(
+      !sideNavExpanded ? null : this.regionSelRef
+    )
+  }
+
+  public handleViewerEvent(event: TViewerEvent<'nehuba' | 'threeSurfer'>){
+    switch(event.type) {
+    case EnumViewerEvt.VIEWERLOADED:
+      this.viewerLoaded = event.data
+      break
+    case EnumViewerEvt.VIEWER_CTX:
+      this.viewerModuleSvc.context$.next(event.data)
+      break
+    default:
+    }
+  }
+
+  public disposeCtxMenu(){
+    this.viewerModuleSvc.dismissCtxMenu()
+  }
+}
diff --git a/src/viewerModule/viewerCmp/viewerCmp.style.css b/src/viewerModule/viewerCmp/viewerCmp.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..2f8a5085ce35e8cf8382f3d1ef01b626b14eb5fe
--- /dev/null
+++ b/src/viewerModule/viewerCmp/viewerCmp.style.css
@@ -0,0 +1,52 @@
+:host
+{
+  position: relative;
+}
+
+.explore-btn
+{
+  margin-top: 4.5rem;
+}
+
+.region-text-search-autocomplete-position
+{
+  z-index: 10;
+  position: sticky;
+  top: 0.5rem;
+}
+
+.tab-toggle-container
+{
+  margin-top: 1.5rem;
+}
+
+
+.tab-toggle
+{
+  margin-left: -2rem;
+  padding-right: 0.75rem;
+  margin-right: 1rem;
+  text-align: right;
+}
+
+.accordion-icon
+{
+  width:1.5rem;
+}
+
+.collapse-position
+{
+  z-index: 10;
+  position: sticky;
+  bottom: 0.5rem;
+}
+
+:host >>> .mat-chip-list-wrapper
+{
+  flex-wrap: nowrap;
+}
+
+mat-drawer
+{
+  overflow-x: hidden;
+}
diff --git a/src/viewerModule/viewerCmp/viewerCmp.template.html b/src/viewerModule/viewerCmp/viewerCmp.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..aa2004d86a9df45e61b54719dbd291f627df569e
--- /dev/null
+++ b/src/viewerModule/viewerCmp/viewerCmp.template.html
@@ -0,0 +1,691 @@
+<div class="position-absolute w-100 h-100">
+  <ng-container *ngTemplateOutlet="viewerTmpl">
+  </ng-container>
+</div>
+
+<layout-floating-container [zIndex]="10">
+
+  <!-- Annotation mode -->
+  <div *ngIf="(viewerMode$ | async) === ARIA_LABELS.VIEWER_MODE_ANNOTATING">
+    <mat-drawer-container class="mat-drawer-content-overflow-visible w-100 h-100 position-absolute invisible"
+      [hasBackdrop]="false">
+      <mat-drawer #annotationDrawer="matDrawer"
+        mode="side"
+        (annotation-event-directive)="annotationDrawer.open()"
+        [annotation-event-directive-filter]="['showList']"
+        [autoFocus]="false"
+        [disableClose]="true"
+        class="p-0 pe-all col-10 col-sm-10 col-md-5 col-lg-4 col-xl-3 col-xxl-2">
+        <annotation-list></annotation-list>
+      </mat-drawer>
+
+      <mat-drawer-content class="visible position-relative pe-none">
+
+        <iav-layout-fourcorners>
+
+          <!-- pullable tab top right corner -->
+          <div iavLayoutFourCornersTopLeft class="tab-toggle-container">
+            <div>
+              <ng-container *ngTemplateOutlet="tabTmpl_defaultTmpl; context: {
+                matColor: 'primary',
+                fontIcon: 'fa-list',
+                tooltip: 'Annotation list',
+                click: annotationDrawer.toggle.bind(annotationDrawer)
+              }">
+              </ng-container>
+            </div>
+
+            <annotating-tools-panel class="z-index-10">
+            </annotating-tools-panel>
+          </div>
+
+          <div iavLayoutFourCornersTopRight>
+            <mat-card class="mat-card-sm pe-all m-4">
+              <span>
+                Annotating
+              </span>
+              <button mat-icon-button
+                [matTooltip]="ARIA_LABELS.EXIT_ANNOTATION_MODE"
+                color="warn"
+                annotation-switch
+                annotation-switch-mode="off">
+                <i class="fas fa-times"></i>
+              </button>
+            </mat-card>
+          </div>
+        </iav-layout-fourcorners>
+
+
+      </mat-drawer-content>
+    </mat-drawer-container>
+    <!-- <annotation-message></annotation-message> -->
+  </div>
+
+  <!-- top drawer -->
+  <mat-drawer-container
+    [hidden]="viewerMode$ | async"
+    [iav-switch-initstate]="false"
+    iav-switch
+    #sideNavTopSwitch="iavSwitch"
+    class="mat-drawer-content-overflow-visible w-100 h-100 position-absolute invisible"
+    [hasBackdrop]="false">
+
+    <!-- sidenav-content -->
+
+    <!-- (closedStart)="sideNavwFullLeftSwitch.switchState && matDrawerLeft.close()"
+    (openedStart)="sideNavFullLeftSwitch.switchState && matDrawerLeft.open()" -->
+    <mat-drawer class="box-shadow-none border-0 pe-none bg-none col-10 col-sm-10 col-md-5 col-lg-4 col-xl-3 col-xxl-2"
+      mode="side"
+      [attr.data-mat-drawer-top-open]="matDrawerTop.opened"
+      [opened]="sideNavTopSwitch.switchState"
+      [autoFocus]="false"
+      [disableClose]="true"
+      (openedChange)="handleSideNavAnimationDone($event)"
+      #matDrawerTop="matDrawer">
+
+      <div class="h-0 w-100 region-text-search-autocomplete-position">
+        <ng-container *ngTemplateOutlet="autocompleteTmpl; context: { showTour: true }">
+        </ng-container>
+      </div>
+
+      <button mat-raised-button
+        *ngIf="!(alwaysHideMinorPanel$ | async)"
+        [attr.aria-label]="ARIA_LABELS.EXPAND"
+        (click)="sideNavFullLeftSwitch && sideNavFullLeftSwitch.open()"
+        class="explore-btn pe-all w-100"
+        [ngClass]="{
+          'darktheme': iavRegion.rgbDarkmode === true,
+          'lighttheme': iavRegion.rgbDarkmode === false
+        }"
+        [style.backgroundColor]="iavRegion?.rgbString || 'accent'">
+        <span class="text iv-custom-comp">
+          Explore
+        </span>
+
+        <div class="hidden"
+          iav-region
+          [region]="(selectedRegions$ | async) && (selectedRegions$ | async)[0]"
+          #iavRegion="iavRegion">
+        </div>
+
+      </button>
+    </mat-drawer>
+
+    <mat-drawer-content class="visible position-relative pe-none">
+
+      <iav-layout-fourcorners [iav-layout-fourcorners-cnr-cntr-ngclass]="{'w-100': true}">
+
+        <!-- pullable tab top left corner -->
+        <div iavLayoutFourCornersTopLeft class="d-flex flex-nowrap w-100">
+
+          <!-- top left -->
+          <div class="flex-grow-1 d-flex flex-nowrap align-items-start">
+
+            <div *ngIf="viewerLoaded"
+              class="pe-all tab-toggle-container"
+              (click)="sideNavTopSwitch && sideNavTopSwitch.toggle()"
+              quick-tour
+              [quick-tour-description]="quickTourRegionSearch.description"
+              [quick-tour-order]="quickTourRegionSearch.order"
+              #regionSearchQuickTour="quickTour">
+              <ng-container *ngTemplateOutlet="tabTmpl; context: {
+                isOpen: sideNavTopSwitch.switchState,
+                regionSelected: selectedRegions$ | async,
+                iavAdditionallayers: iavAdditionalLayers$ | async
+              }">
+              </ng-container>
+            </div>
+
+            <iav-cmp-viewer-nehuba-status *ngIf="(useViewer$ | async) === 'nehuba'"
+              class="pe-all mt-2 muted-7">
+            </iav-cmp-viewer-nehuba-status>
+          </div>
+
+          <!-- top right -->
+          <div class="flex-grow-0 d-inline-flex align-items-start">
+
+            <!-- signin banner at top right corner -->
+
+
+            <top-menu-cmp class="mt-3 mr-2 d-inline-block"
+              [ismobile]="ismobile"
+              [viewerLoaded]="viewerLoaded">
+            </top-menu-cmp>
+
+            <div *ngIf="viewerLoaded"
+              class="iv-custom-comp bg card m-2 mat-elevation-z2"
+              quick-tour
+              [quick-tour-description]="quickTourAtlasSelector.description"
+              [quick-tour-order]="quickTourAtlasSelector.order">
+              <atlas-dropdown-selector class="pe-all mt-2">
+              </atlas-dropdown-selector>
+            </div>
+          </div>
+        </div>
+
+      </iav-layout-fourcorners>
+
+    </mat-drawer-content>
+  </mat-drawer-container>
+
+  <!-- full left drawer -->
+  <mat-drawer-container
+    [hidden]="viewerMode$ | async"
+    [iav-switch-initstate]="!(alwaysHideMinorPanel$ | async)"
+    iav-switch
+    #sideNavFullLeftSwitch="iavSwitch"
+    class="mat-drawer-content-overflow-visible w-100 h-100 position-absolute invisible"
+    [hasBackdrop]="false">
+
+    <!-- sidenav-content -->
+    <mat-drawer class="darker-bg iv-custom-comp visible col-10 col-sm-10 col-md-5 col-lg-4 col-xl-3 col-xxl-2 d-flex flex-column pe-all"
+      mode="push"
+      [opened]="sideNavTopSwitch.switchState && sideNavFullLeftSwitch.switchState"
+      [attr.data-mat-drawer-fullleft-open]="matDrawerLeft.opened"
+      [autoFocus]="false"
+      #matDrawerLeft="matDrawer"
+      (openedChange)="$event && sideNavFullLeftSwitch.open()"
+      [@openClose]="sideNavTopSwitch.switchState && sideNavFullLeftSwitch.switchState ? 'open' : 'closed'"
+      (@openClose.done)="$event.toState === 'closed' && matDrawerLeft.close()"
+      [disableClose]="true">
+
+      <!-- check if preview volume -->
+      <div *ngIf="overlaySidenav$ | async as overlaySideNav" class="position-relative d-flex flex-column h-100">
+        <div class="position-relative ml-15px-n mr-15px-n">
+
+          <!-- back btn -->
+          <button mat-button
+            (click)="clearPreviewingDataset()"
+            [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>
+
+          <ng-template #genericInfoVCR>
+          </ng-template>
+        </div>
+      </div>
+
+      <div [ngClass]="{
+        'invisible overflow-hidden h-0': overlaySidenav$ | async,
+        'h-100': !(overlaySidenav$ | async)
+      }" class="position-relative d-flex flex-column">
+
+        <ng-container *ngTemplateOutlet="sidenavRegionTmpl">
+        </ng-container>
+
+        <!-- TODO dataset preview will become deprecated in the future.
+        Regional feature/data feature will replace it -->
+        <!-- <div class="hidden"
+          iav-shown-dataset
+          #iavShownDataset="iavShownDataset">
+        </div> -->
+
+      </div>
+    </mat-drawer>
+
+    <!-- main-content -->
+    <mat-drawer-content class="visible position-relative" [hidden]="viewerMode$ | async">
+
+      <iav-layout-fourcorners [iav-layout-fourcorners-cnr-cntr-ngclass]="{'w-100': true}">
+
+        <!-- bottom left corner (atlas selector and currently selected) -->
+        <div iavLayoutFourCornersBottomLeft class="d-inline-flex align-items-center mb-4 ml-2 w-100">
+
+          <!-- atlas selector -->
+          <atlas-layer-selector *ngIf="viewerLoaded && !(isStandaloneVolumes$ | async)"
+            #alSelector="atlasLayerSelector"
+            (iav-outsideClick)="alSelector.selectorExpanded = false">
+          </atlas-layer-selector>
+
+          <!-- chips -->
+          <div *ngIf="parcellationSelected$ | async" class="flex-grow-0 p-1 pr-2 flex-shrink-1 overflow-y-hidden overflow-x-auto pe-all">
+
+            <viewer-state-breadcrumb
+              (on-item-click)="handleChipClick()">
+            </viewer-state-breadcrumb>
+          </div>
+        </div>
+
+      </iav-layout-fourcorners>
+
+    </mat-drawer-content>
+  </mat-drawer-container>
+
+</layout-floating-container>
+
+<!-- viewer tmpl -->
+<ng-template #viewerTmpl>
+
+  <iav-layout-fourcorners>
+    <div iavLayoutFourCornersContent
+      class="w-100 h-100 position-absolute">
+      <div class="h-100 w-100 overflow-hidden position-relative"
+        ctx-menu-host
+        [ctx-menu-host-tmpl]="viewerCtxMenuTmpl">
+
+        <ng-container [ngSwitch]="useViewer$ | async">
+
+          <!-- nehuba viewer -->
+          <iav-cmp-viewer-nehuba-glue class="d-block w-100 h-100 position-absolute left-0 top-0"
+            *ngSwitchCase="'nehuba'"
+            (viewerEvent)="handleViewerEvent($event)"
+            [selectedTemplate]="templateSelected$ | async"
+            [selectedParcellation]="parcellationSelected$ | async"
+            #iavCmpViewerNehubaGlue="iavCmpViewerNehubaGlue">
+          </iav-cmp-viewer-nehuba-glue>
+
+          <!-- three surfer (free surfer viewer) -->
+          <three-surfer-glue-cmp class="d-block w-100 h-100 position-absolute left-0 top-0"
+            *ngSwitchCase="'threeSurfer'"
+            (viewerEvent)="handleViewerEvent($event)"
+            [selectedTemplate]="templateSelected$ | async"
+            [selectedParcellation]="parcellationSelected$ | async">
+          </three-surfer-glue-cmp>
+
+          <!-- if not supported, show not supported message -->
+          <div *ngSwitchCase="'notsupported'">Template not supported by any of the viewers</div>
+
+          <!-- by default, show splash screen -->
+          <div *ngSwitchDefault>
+            <ui-splashscreen class="position-absolute left-0 top-0">
+            </ui-splashscreen>
+          </div>
+        </ng-container>
+
+      </div>
+    </div>
+  </iav-layout-fourcorners>
+</ng-template>
+
+<!-- auto complete search box -->
+
+<ng-template #autocompleteTmpl let-showTour="showTour">
+  <div class="iv-custom-comp bg card w-100 mat-elevation-z8 pe-all">
+    <region-text-search-autocomplete class="w-100 pt-2 flex-shrink-0 flex-grow-0">
+    </region-text-search-autocomplete>
+
+    <div class="w-100 h-100 position-absolute pe-none"
+      *ngIf="showTour"
+      #regionSelRef>
+    </div>
+  </div>
+</ng-template>
+
+<!-- template for rendering tab -->
+<ng-template #tabTmpl
+  let-isOpen="isOpen"
+  let-regionSelected="regionSelected"
+  let-iavAdditionallayers="iavAdditionallayers">
+
+  <!-- if mat drawer is open -->
+  <ng-template [ngIf]="isOpen" [ngIfElse]="tabTmpl_closedTmpl">
+    <ng-container *ngTemplateOutlet="tabTmpl_defaultTmpl; context: {
+      matColor: 'basic',
+      fontIcon: 'fa-chevron-left'
+    }">
+    </ng-container>
+  </ng-template>
+
+  <!-- if matdrawer is closed -->
+  <ng-template #tabTmpl_closedTmpl>
+
+    <!-- if additional layers are being shown -->
+    <ng-template [ngIf]="iavAdditionallayers?.length > 0" [ngIfElse]="tabTmpl_noAdditionalLayers">
+      <ng-container *ngTemplateOutlet="tabTmpl_defaultTmpl; context: {
+        matColor: 'accent',
+        fontIcon: 'fa-database',
+        tooltip: 'Explore dataset preview'
+      }">
+      </ng-container>
+    </ng-template>
+
+    <!-- if additional layers not not being shown -->
+    <ng-template #tabTmpl_noAdditionalLayers>
+
+      <!-- if region selected > 0 -->
+      <ng-template [ngIf]="regionSelected?.length > 0" [ngIfElse]="tabTmpl_nothingSelected">
+        <div class="hidden"
+          iav-region
+          [region]="regionSelected[0]"
+          #tabTmpl_iavRegion="iavRegion">
+        </div>
+
+        <ng-container *ngTemplateOutlet="tabTmpl_defaultTmpl; context: {
+          matColor: 'accent',
+          customColor: tabTmpl_iavRegion.rgbString,
+          customColorDarkmode: tabTmpl_iavRegion.rgbDarkmode,
+          fontIcon: 'fa-brain',
+          tooltip: 'Explore ' + tabTmpl_iavRegion.region.name
+        }">
+
+        </ng-container>
+      </ng-template>
+
+      <!-- nothing is selected -->
+      <ng-template #tabTmpl_nothingSelected>
+        <ng-container *ngTemplateOutlet="tabTmpl_defaultTmpl; context: {
+          matColor: 'primary',
+          fontIcon: 'fa-sitemap',
+          tooltip: 'Explore regions'
+        }">
+        </ng-container>
+      </ng-template>
+    </ng-template>
+  </ng-template>
+
+
+</ng-template>
+<ng-template #tabTmpl_defaultTmpl
+  let-matColor="matColor"
+  let-fontIcon="fontIcon"
+  let-customColor="customColor"
+  let-customColorDarkmode="customColorDarkmode"
+  let-tooltip="tooltip"
+  let-click="click">
+  <!-- (click)="sideNavMasterSwitch.toggle()" -->
+  <button mat-raised-button
+    [attr.aria-label]="ARIA_LABELS.TOGGLE_SIDE_PANEL"
+    [matTooltip]="tooltip"
+    class="pe-all tab-toggle"
+    [ngClass]="{
+      'darktheme': customColorDarkmode === true,
+      'lighttheme': customColorDarkmode === false
+    }"
+    (click)="click && click()"
+    [style.backgroundColor]="customColor"
+    [color]="(!customColor && matColor) ? matColor : null">
+
+    <span [ngClass]="{'iv-custom-comp  text': !!customColor}">
+      <i class="fas" [ngClass]="fontIcon || 'fa-question'"></i>
+    </span>
+  </button>
+</ng-template>
+
+<!-- region sidenav tmpl -->
+<ng-template #sidenavRegionTmpl>
+
+  <!-- region search autocomplete  -->
+  <!-- [@openCloseAnchor]="sideNavFullLeftSwitch.switchState ? 'open' : 'closed'" -->
+  <div class="h-0 w-100 region-text-search-autocomplete-position">
+    <ng-container *ngTemplateOutlet="autocompleteTmpl">
+    </ng-container>
+  </div>
+
+  <div class="flex-shrink-1 flex-grow-1 d-flex flex-column"
+    [ngClass]="{'region-populated': (selectedRegions$ | async).length > 0 }">
+    <!-- region detail -->
+    <ng-container *ngIf="selectedRegions$ | async as selectedRegions; else selectRegionErrorTmpl">
+
+      <!-- single-region-wrapper -->
+      <ng-template [ngIf]="selectedRegions.length === 1" [ngIfElse]="multiRegionWrapperTmpl">
+        <!-- a series of bugs result in requiring this hacky -->
+        <!-- see https://github.com/HumanBrainProject/interactive-viewer/issues/698 -->
+        <ng-container *ngTemplateOutlet="singleRegionTmpl; context: { region: (regionOfInterest$ | async) }">
+        </ng-container>
+      </ng-template>
+
+      <!-- multi region wrapper -->
+      <ng-template #multiRegionWrapperTmpl>
+        <ng-container *ngTemplateOutlet="multiRegionTmpl; context: {
+          regions: selectedRegions
+        }">
+        </ng-container>
+        <!-- This is a wrapper for multiregion consisting of {{ selectedRegions.length }} regions -->
+      </ng-template>
+
+      <!-- place holder if length === 0 -->
+      <ng-container *ngIf="selectedRegions.length === 0">
+        <ng-container *ngTemplateOutlet="singleRegionTmpl; context: { region: false }">
+        </ng-container>
+      </ng-container>
+    </ng-container>
+
+    <div class="spacer">
+    </div>
+  </div>
+
+  <!-- collapse btn -->
+  <ng-container *ngTemplateOutlet="collapseBtn">
+  </ng-container>
+</ng-template>
+
+
+<!-- single region tmpl -->
+<ng-template #singleRegionTmpl let-region="region">
+  <!-- region detail -->
+  <region-menu
+    [region]="region"
+    class="flex-grow-1 bs-border-box ml-15px-n mr-15px-n mat-elevation-z4">
+  </region-menu>
+</ng-template>
+
+
+<!-- expansion tmpl -->
+<ng-template #ngMatAccordionTmpl
+  let-title="title"
+  let-desc="desc"
+  let-iconClass="iconClass"
+  let-iconTooltip="iconTooltip"
+  let-iavNgIf="iavNgIf"
+  let-content="content">
+  <mat-expansion-panel
+    [attr.data-opened]="expansionPanel.expanded"
+    [attr.data-mat-expansion-title]="title"
+    hideToggle
+    *ngIf="iavNgIf"
+    #expansionPanel="matExpansionPanel">
+
+    <mat-expansion-panel-header>
+
+      <!-- title -->
+      <mat-panel-title>
+        {{ title }}
+      </mat-panel-title>
+
+      <!-- desc + icon -->
+      <mat-panel-description class="d-flex align-items-center justify-content-end"
+        [matTooltip]="iconTooltip">
+        <span class="mr-3">{{ desc }}</span>
+        <span class="accordion-icon d-inline-flex justify-content-center">
+          <i [class]="iconClass"></i>
+        </span>
+      </mat-panel-description>
+
+    </mat-expansion-panel-header>
+
+    <!-- content -->
+    <ng-template matExpansionPanelContent>
+      <ng-container *ngTemplateOutlet="content; context: { expansionPanel: expansionPanel }">
+      </ng-container>
+    </ng-template>
+  </mat-expansion-panel>
+</ng-template>
+
+<!-- misc dataset (e.g. PLI) -->
+<!-- <ng-template #sidenavDsPreviewTmpl let-file="file" let-filename="filename" let-datasetId="datasetId">
+  <div class="w-100 flex-grow-1 d-flex flex-column">
+
+    Previewing misc dataset
+
+    <ng-container *ngTemplateOutlet="collapseBtn">
+    </ng-container>
+  </div>
+</ng-template> -->
+
+<!-- select region error... for whatever reason -->
+<ng-template #selectRegionErrorTmpl>
+  SELECT REGION ERROR
+</ng-template>
+
+
+<!-- multi region tmpl -->
+<ng-template #multiRegionTmpl let-regions="regions">
+  <ng-template [ngIf]="regions.length > 0" [ngIfElse]="regionPlaceholderTmpl">
+    <region-menu
+      [region]="{ name: CONST.MULTI_REGION_SELECTION }"
+      class="bs-border-box ml-15px-n mr-15px-n mat-elevation-z4">
+    </region-menu>
+
+    <!-- other regions detail accordion -->
+    <mat-accordion class="bs-border-box ml-15px-n mr-15px-n mt-2">
+
+      <!-- Multi regions include -->
+      <ng-template #multiRegionInclTmpl>
+        <mat-chip-list>
+          <mat-chip *ngFor="let r of regions"
+            iav-region
+            [region]="r"
+            [ngClass]="{
+              'darktheme':regionDirective.rgbDarkmode === true,
+              'lighttheme': regionDirective.rgbDarkmode === false
+            }"
+            [style.backgroundColor]="regionDirective.rgbString"
+            #regionDirective="iavRegion">
+            <span class="iv-custom-comp text text-truncate d-inline pl-4">
+              {{ r.name }}
+            </span>
+          </mat-chip>
+        </mat-chip-list>
+      </ng-template>
+
+      <ng-container *ngTemplateOutlet="ngMatAccordionTmpl; context: {
+        title: 'Brain regions',
+        desc: regions.length,
+        iconClass: 'fas fa-brain',
+        iavNgIf: true,
+        content: multiRegionInclTmpl
+      }">
+      </ng-container>
+
+    </mat-accordion>
+  </ng-template>
+</ng-template>
+
+<!-- collapse btn -->
+<ng-template #collapseBtn>
+
+  <div class="h-0 w-100 collapse-position d-flex flex-column justify-content-end align-items-center">
+
+    <button mat-raised-button class="mat-elevation-z8"
+      [attr.aria-label]="ARIA_LABELS.COLLAPSE"
+      (click)="sideNavFullLeftSwitch.close()"
+      color="basic">
+      <i class="fas fa-chevron-up"></i>
+      <span>
+        collapse
+      </span>
+    </button>
+  </div>
+</ng-template>
+
+<!-- region tmpl placeholder -->
+<ng-template #regionPlaceholderTmpl>
+  <div class="placeholder-region-detail bs-border-box ml-15px-n mr-15px-n mat-elevation-z4">
+    <span class="text-muted">
+      Select a region by clicking on the viewer or search from above
+    </span>
+  </div>
+</ng-template>
+
+<!-- context menu template -->
+<ng-template #viewerCtxMenuTmpl let-tmplRefs="tmplRefs">
+  <mat-card class="p-0 d-flex flex-column"
+    [iav-key-listener]="[{type: 'keydown', target: 'document', capture: true}]"
+    (iav-key-event)="disposeCtxMenu()"
+    (iav-outsideClick)="disposeCtxMenu()">
+    <mat-card-content *ngFor="let tmplRef of tmplRefs"
+      class="m-0"
+      [ngStyle]="{order: tmplRef.order || 0}">
+      <mat-divider></mat-divider>
+
+      <!-- template provided -->
+      <ng-template [ngIf]="tmplRef.tmpl"
+        [ngIfElse]="fallbackTmpl"
+        [ngTemplateOutlet]="tmplRef.tmpl"
+        [ngTemplateOutletContext]="{$implicit: tmplRef.data}">
+      </ng-template>
+
+      <!-- template not provided -->
+      <ng-template #fallbackTmpl>
+        {{ tmplRef.data.message || 'test' }}
+      </ng-template>
+
+      <mat-divider></mat-divider>
+    </mat-card-content>
+  </mat-card>
+</ng-template>
+
+<!-- viewer status ctx menu -->
+<ng-template #viewerStatusCtxMenu let-data>
+  <mat-list>
+
+    <!-- ref space & position -->
+    <ng-container [ngSwitch]="data.context.viewerType">
+
+      <!-- volumetric i.e. nehuba -->
+      <ng-container *ngSwitchCase="'nehuba'">
+        <mat-list-item>
+          <span mat-line>
+            {{ data.context.payload.mouse.real | nmToMm | addUnitAndJoin : '' }} (mm)
+          </span>
+          <span mat-line class="text-muted">
+            <i class="fas fa-map"></i>
+            <span>
+              {{ data.metadata.template.displayName || data.metadata.template.name }}
+            </span>
+          </span>
+        </mat-list-item>
+      </ng-container>
+
+      <ng-container *ngSwitchCase="'threeSurfer'">
+        <mat-list-item *ngIf="data?.context?.payload?.faceIndex && data?.context?.payload?.vertexIndices">
+          <span mat-line>
+            face#{{ data.context.payload.faceIndex }}
+          </span>
+          <span mat-line>
+            vertices#{{ data.context.payload.vertexIndices | addUnitAndJoin : '' }}
+          </span>
+          <span mat-line class="text-muted">
+            <i class="fas fa-map"></i>
+            <span>
+              {{ data.context.payload.fsversion }}
+            </span>
+          </span>
+        </mat-list-item>
+      </ng-container>
+
+      <ng-container *ngSwitchDefault>
+        DEFAULT
+      </ng-container>
+    </ng-container>
+  </mat-list>
+</ng-template>
+
+<ng-template #viewerStatusRegionCtxMenu let-data>
+  <!-- hovered ROIs -->
+  <mat-list>
+    <mat-list-item *ngFor="let hoveredR of data.metadata.hoveredRegions; let first = first">
+      <mat-divider class="top-0" *ngIf="!first"></mat-divider>
+      <span mat-line>
+        {{ hoveredR.displayName || hoveredR.name }}
+      </span>
+      <span mat-line class="text-muted">
+        <i class="fas fa-brain"></i>
+        <span>
+          Brain region
+        </span>
+      </span>
+
+      <!-- lookup region -->
+      <button mat-icon-button
+        (click)="selectRoi(hoveredR)"
+        ctx-menu-dismiss>
+        <i class="fas fa-search"></i>
+      </button>
+    </mat-list-item>
+  </mat-list>
+</ng-template>
diff --git a/src/viewerModule/viewerStateBreadCrumb/breadcrumb/breadcrumb.component.ts b/src/viewerModule/viewerStateBreadCrumb/breadcrumb/breadcrumb.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..66ca3b4e1e7ea6d3404d562205ddae05488fa138
--- /dev/null
+++ b/src/viewerModule/viewerStateBreadCrumb/breadcrumb/breadcrumb.component.ts
@@ -0,0 +1,129 @@
+import { Component, EventEmitter, Output, Pipe, PipeTransform } from "@angular/core";
+import { IQuickTourData } from "src/ui/quickTour";
+import { CONST, ARIA_LABELS, QUICKTOUR_DESC } from 'common/constants'
+import { select, Store } from "@ngrx/store";
+import { viewerStateContextedSelectedRegionsSelector, viewerStateGetOverlayingAdditionalParcellations, viewerStateParcVersionSelector, viewerStateSelectedParcellationSelector } from "src/services/state/viewerState/selectors";
+import { distinctUntilChanged, map } from "rxjs/operators";
+import { viewerStateHelperSelectParcellationWithId, viewerStateRemoveAdditionalLayer, viewerStateSetSelectedRegions } from "src/services/state/viewerState.store.helper";
+import { ngViewerActionClearView, ngViewerSelectorClearViewEntries } from "src/services/state/ngViewerState.store.helper";
+import { OVERWRITE_SHOW_DATASET_DIALOG_TOKEN } from "src/util/interfaces";
+import { TDatainfos } from "src/util/siibraApiConstants/types";
+
+@Component({
+  selector: 'viewer-state-breadcrumb',
+  templateUrl: './breadcrumb.template.html',
+  styleUrls: [
+    './breadcrumb.style.css'
+  ],
+  providers: [
+    {
+      provide: OVERWRITE_SHOW_DATASET_DIALOG_TOKEN,
+      useValue: null
+    }
+  ]
+})
+
+export class ViewerStateBreadCrumb {
+
+  public CONST = CONST
+  public ARIA_LABELS = ARIA_LABELS
+
+  @Output('on-item-click')
+  onChipClick = new EventEmitter()
+
+  public quickTourChips: IQuickTourData = {
+    order: 5,
+    description: QUICKTOUR_DESC.CHIPS,
+  }
+
+  public clearViewKeys$ = this.store$.pipe(
+    select(ngViewerSelectorClearViewEntries)
+  )
+
+  public selectedAdditionalLayers$ = this.store$.pipe(
+    select(viewerStateGetOverlayingAdditionalParcellations),
+  )
+
+  public parcellationSelected$ = this.store$.pipe(
+    select(viewerStateSelectedParcellationSelector),
+    distinctUntilChanged(),
+  )
+
+  public selectedRegions$ = this.store$.pipe(
+    select(viewerStateContextedSelectedRegionsSelector),
+    distinctUntilChanged(),
+  )
+
+  public selectedLayerVersions$ = this.store$.pipe(
+    select(viewerStateParcVersionSelector),
+    map(arr => arr.map(item => {
+      const overwrittenName = item['@version'] && item['@version']['name']
+      return overwrittenName
+        ? { ...item, displayName: overwrittenName }
+        : item
+    }))
+  )
+
+
+  constructor(private store$: Store<any>){
+
+  }
+
+  handleChipClick(){
+    this.onChipClick.emit(null)
+  }
+
+  public clearSelectedRegions(){
+    this.store$.dispatch(
+      viewerStateSetSelectedRegions({
+        selectRegions: []
+      })
+    )
+  }
+
+  public unsetClearViewByKey(key: string){
+    this.store$.dispatch(
+      ngViewerActionClearView({ payload: {
+        [key]: false
+      }})
+    )
+  }
+
+  public clearAdditionalLayer(layer: { ['@id']: string }){
+    this.store$.dispatch(
+      viewerStateRemoveAdditionalLayer({
+        payload: layer
+      })
+    )
+  }
+
+  public selectParcellation(parc: any) {
+    this.store$.dispatch(
+      viewerStateHelperSelectParcellationWithId({
+        payload: parc
+      })
+    )
+  }
+
+  public bindFns(fns){
+    return () => {
+      for (const [ fn, ...arg] of fns) {
+        fn(...arg)
+      }
+    }
+  }
+
+}
+
+@Pipe({
+  name: 'originalDatainfoPriorityPipe'
+})
+
+export class OriginalDatainfoPipe implements PipeTransform{
+  public transform(originalDatainfo: TDatainfos[]): TDatainfos[]{
+    if (originalDatainfo.some(info => info.urls.length > 0)) {
+      return originalDatainfo.filter(info => info.urls.length > 0)
+    }
+    return originalDatainfo.slice(0,1)
+  }
+}
diff --git a/src/viewerModule/viewerStateBreadCrumb/breadcrumb/breadcrumb.style.css b/src/viewerModule/viewerStateBreadCrumb/breadcrumb/breadcrumb.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/viewerModule/viewerStateBreadCrumb/breadcrumb/breadcrumb.template.html b/src/viewerModule/viewerStateBreadCrumb/breadcrumb/breadcrumb.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..65f26e75ad94a9184c80db66402d5ced32805349
--- /dev/null
+++ b/src/viewerModule/viewerStateBreadCrumb/breadcrumb/breadcrumb.template.html
@@ -0,0 +1,206 @@
+<mat-chip-list
+  quick-tour
+  [quick-tour-description]="quickTourChips.description"
+  [quick-tour-order]="quickTourChips.order">
+
+  <!-- additional layer -->
+
+  <ng-container>
+    <ng-container *ngTemplateOutlet="currParcellationTmpl; context: {
+      addParc: (selectedAdditionalLayers$ | async),
+      parc: (parcellationSelected$ | async)
+    }">
+    </ng-container>
+  </ng-container>
+
+  <!-- any selected region(s) -->
+  <ng-container>
+    <ng-container *ngTemplateOutlet="selectedRegionTmpl">
+    </ng-container>
+  </ng-container>
+</mat-chip-list>
+
+
+<!-- parcellation chip / region chip -->
+<ng-template #currParcellationTmpl let-parc="parc" let-addParc="addParc">
+  <div [matMenuTriggerFor]="layerVersionMenu"
+    [matMenuTriggerData]="{ layerVersionMenuTrigger: layerVersionMenuTrigger }"
+    #layerVersionMenuTrigger="matMenuTrigger">
+
+    <ng-template [ngIf]="addParc.length > 0" [ngIfElse]="defaultParcTmpl">
+      <ng-container *ngFor="let p of addParc">
+        <ng-container *ngTemplateOutlet="chipTmpl; context: {
+          parcel: p,
+          selected: true,
+          dismissable: true,
+          ariaLabel: ARIA_LABELS.PARC_VER_SELECT,
+          onclick: layerVersionMenuTrigger.toggleMenu.bind(layerVersionMenuTrigger)
+        }">
+        </ng-container>
+      </ng-container>
+    </ng-template>
+    <ng-template #defaultParcTmpl>
+      <ng-template [ngIf]="parc">
+
+        <ng-container *ngTemplateOutlet="chipTmpl; context: {
+          parcel: parc,
+          selected: false,
+          dismissable: false,
+          ariaLabel: ARIA_LABELS.PARC_VER_SELECT,
+          onclick: layerVersionMenuTrigger.toggleMenu.bind(layerVersionMenuTrigger)
+        }">
+        </ng-container>
+      </ng-template>
+    </ng-template>
+  </div>
+</ng-template>
+
+
+<ng-template #selectedRegionTmpl>
+
+  <!-- regions chip -->
+  <ng-template [ngIf]="selectedRegions$ | async" let-selectedRegions="ngIf">
+    <!-- if regions.length > 1 -->
+    <!-- use group chip -->
+    <ng-template [ngIf]="selectedRegions.length > 1" [ngIfElse]="singleRegionChipTmpl">
+      <mat-chip
+        color="primary"
+        selected
+        (click)="handleChipClick()"
+        class="pe-all position-relative z-index-1 ml-8-n">
+        <span class="iv-custom-comp text text-truncate d-inline pl-4">
+          {{ CONST.MULTI_REGION_SELECTION }}
+        </span>
+        <mat-icon
+          (click)="clearSelectedRegions()"
+          fontSet="fas"
+          iav-stop="click"
+          fontIcon="fa-times">
+        </mat-icon>
+      </mat-chip>
+    </ng-template>
+
+    <!-- if reginos.lengt === 1 -->
+    <!-- use single region chip -->
+    <ng-template #singleRegionChipTmpl>
+      <ng-container *ngFor="let r of selectedRegions">
+
+        <!-- region chip for discrete map -->
+        <mat-chip
+          (click)="handleChipClick()"
+          [region]="r"
+          class="pe-all position-relative z-index-1 ml-8-n"
+          [ngClass]="{
+            'darktheme':regionDirective.rgbDarkmode === true,
+            'lighttheme': regionDirective.rgbDarkmode === false
+          }"
+          [style.backgroundColor]="regionDirective.rgbString"
+          iav-region
+          #regionDirective="iavRegion">
+          <span class="iv-custom-comp text text-truncate d-inline pl-4">
+            {{ r.name }}
+          </span>
+          <mat-icon
+            class="iv-custom-comp text"
+            (click)="clearSelectedRegions()"
+            fontSet="fas"
+            iav-stop="click"
+            fontIcon="fa-times">
+          </mat-icon>
+        </mat-chip>
+
+        <!-- chips for previewing origin datasets/continous map -->
+        <ng-container *ngFor="let originDataset of (r.originDatasets || []); let index = index">
+
+          <mat-chip *ngFor="let key of clearViewKeys$ | async"
+            (click)="handleChipClick()"
+            class="pe-all position-relative ml-8-n">
+            <span class="pl-4">
+              {{ key }}
+            </span>
+            <mat-icon (click)="unsetClearViewByKey(key)"
+              fontSet="fas"
+              iav-stop="click"
+              fontIcon="fa-times">
+
+            </mat-icon>
+          </mat-chip>
+        </ng-container>
+
+      </ng-container>
+    </ng-template>
+  </ng-template>
+
+</ng-template>
+
+<!-- layer version selector -->
+<mat-menu #layerVersionMenu
+  class="bg-none box-shadow-none"
+  [aria-label]="ARIA_LABELS.PARC_VER_CONTAINER"
+  [hasBackdrop]="false">
+  <ng-template matMenuContent let-layerVersionMenuTrigger="layerVersionMenuTrigger">
+    <div (iav-outsideClick)="layerVersionMenuTrigger.closeMenu()">
+      <ng-container *ngFor="let parcVer of selectedLayerVersions$ | async">
+        <ng-container *ngIf="parcellationSelected$ | async as selectedParcellation">
+
+          <ng-container *ngTemplateOutlet="chipTmpl; context: {
+            parcel: parcVer,
+            selected: selectedParcellation['@id'] === parcVer['@id'],
+            dismissable: false,
+            class: 'w-100',
+            ariaLabel: parcVer.displayName || parcVer.name,
+            onclick: bindFns([
+              [ selectParcellation.bind(this), parcVer ],
+              [ layerVersionMenuTrigger.closeMenu.bind(layerVersionMenuTrigger) ]
+            ])
+          }">
+          </ng-container>
+        </ng-container>
+        <div class="mt-1"></div>
+      </ng-container>
+    </div>
+  </ng-template>
+</mat-menu>
+
+
+<ng-template #chipTmpl
+  let-parcel="parcel"
+  let-selected="selected"
+  let-dismissable="dismissable"
+  let-chipClass="class"
+  let-ariaLabel="ariaLabel"
+  let-onclick="onclick">
+  <mat-chip class="pe-all position-relative z-index-2 d-inline-flex justify-content-between"
+    [ngClass]="chipClass"
+    [attr.aria-label]="ariaLabel"
+    (click)="onclick && onclick()"
+    [selected]="selected">
+
+    <span class="ws-no-wrap">
+      {{ parcel?.groupName ? (parcel?.groupName + ' - ') : '' }}{{ parcel && (parcel.displayName || parcel.name) }}
+    </span>
+
+    <!-- info icon -->
+    <ng-container *ngFor="let originDatainfo of ((parcel?.originDatainfos || []) | originalDatainfoPriorityPipe)">
+      
+      <mat-icon
+        fontSet="fas"
+        fontIcon="fa-info-circle"
+        iav-stop="click"
+        iav-dataset-show-dataset-dialog
+        [iav-dataset-show-dataset-dialog-name]="originDatainfo.name"
+        [iav-dataset-show-dataset-dialog-description]="originDatainfo.description"
+        [iav-dataset-show-dataset-dialog-urls]="originDatainfo.urls">
+      </mat-icon>
+
+    </ng-container>
+
+    <!-- dismiss icon -->
+    <mat-icon
+      *ngIf="dismissable"
+      (click)="clearAdditionalLayer(parcel); $event.stopPropagation()"
+      fontSet="fas"
+      fontIcon="fa-times">
+    </mat-icon>
+  </mat-chip>
+</ng-template>
diff --git a/src/viewerModule/viewerStateBreadCrumb/module.ts b/src/viewerModule/viewerStateBreadCrumb/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6921a1590dce3f456785d32c985383eee4c528a6
--- /dev/null
+++ b/src/viewerModule/viewerStateBreadCrumb/module.ts
@@ -0,0 +1,30 @@
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { ParcellationRegionModule } from "src/atlasComponents/parcellationRegion";
+import { KgDatasetModule } from "src/atlasComponents/regionalFeatures/bsFeatures/kgDataset";
+import { QuickTourModule } from "src/ui/quickTour";
+import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module";
+import { UtilModule } from "src/util";
+import { OriginalDatainfoPipe, ViewerStateBreadCrumb } from "./breadcrumb/breadcrumb.component";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    AngularMaterialModule,
+    QuickTourModule,
+    ParcellationRegionModule,
+    KgDatasetModule,
+    UtilModule,
+  ],
+  declarations: [
+    ViewerStateBreadCrumb,
+    OriginalDatainfoPipe,
+  ],
+  exports: [
+    ViewerStateBreadCrumb,
+  ],
+  providers:[
+  ]
+})
+
+export class ViewerStateBreadCrumbModule{}
\ No newline at end of file
diff --git a/src/zipFilesOutput/downloadSingleFile.directive.ts b/src/zipFilesOutput/downloadSingleFile.directive.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1ad93e0fac8a36c607965305291538cacb3108e7
--- /dev/null
+++ b/src/zipFilesOutput/downloadSingleFile.directive.ts
@@ -0,0 +1,32 @@
+import { DOCUMENT } from "@angular/common";
+import { Directive, HostListener, Inject, Input } from "@angular/core";
+import { TZipFileConfig } from "./type";
+
+@Directive({
+  selector: '[single-file-output]',
+  exportAs: 'singleFileOutput'
+})
+
+export class SingleFileOutput {
+
+  @Input('single-file-output')
+  singleFile: TZipFileConfig
+
+  @HostListener('click')
+  onClick(){
+    const anchor = this.doc.createElement('a')
+    const blob = new Blob([this.singleFile.filecontent], { type: 'text/plain' })
+    anchor.href = URL.createObjectURL(blob)
+    anchor.download = this.singleFile.filename
+
+    this.doc.body.appendChild(anchor)
+    anchor.click()
+    this.doc.body.removeChild(anchor)
+    URL.revokeObjectURL(anchor.href)
+  }
+  constructor(
+    @Inject(DOCUMENT) private doc: Document
+  ){
+
+  }
+}
diff --git a/src/zipFilesOutput/module.ts b/src/zipFilesOutput/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7eeff21ca6a7c3bbb936c018ff11f2a2cd8b541a
--- /dev/null
+++ b/src/zipFilesOutput/module.ts
@@ -0,0 +1,22 @@
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { SingleFileOutput } from "./downloadSingleFile.directive";
+import { ZipFilesOutput } from "./zipFilesOutput.directive";
+
+@NgModule({
+  imports: [
+    CommonModule,
+  ],
+  declarations: [
+    ZipFilesOutput,
+    SingleFileOutput,
+  ],
+  exports: [
+    ZipFilesOutput,
+    SingleFileOutput,
+  ]
+})
+
+export class ZipFilesOutputModule{}
+
+export { TZipFileConfig } from './type'
\ No newline at end of file
diff --git a/src/zipFilesOutput/type.ts b/src/zipFilesOutput/type.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f5e986b36848cd8cea94cad3e4d54dbd17fa495e
--- /dev/null
+++ b/src/zipFilesOutput/type.ts
@@ -0,0 +1,5 @@
+export type TZipFileConfig = {
+  filename: string
+  filecontent: string
+  base64?: boolean
+}
\ No newline at end of file
diff --git a/src/zipFilesOutput/zipFilesOutput.directive.ts b/src/zipFilesOutput/zipFilesOutput.directive.ts
new file mode 100644
index 0000000000000000000000000000000000000000..bcf70bddfaf8e3e84ab97f887ae93ae8e95fcfc6
--- /dev/null
+++ b/src/zipFilesOutput/zipFilesOutput.directive.ts
@@ -0,0 +1,55 @@
+import { Directive, HostListener, Inject, Input } from "@angular/core";
+import { TZipFileConfig } from "./type";
+import * as JSZip from "jszip";
+import { DOCUMENT } from "@angular/common";
+
+@Directive({
+  selector: '[zip-files-output]',
+  exportAs: 'zipFilesOutput'
+})
+
+export class ZipFilesOutput {
+  @Input('zip-files-output')
+  zipFiles: TZipFileConfig[] = []
+
+  @Input('zip-files-output-zip-filename')
+  zipFilename = 'archive.zip'
+
+  @HostListener('click')
+  async onClick(){
+    const zip = new JSZip()
+    for (const zipFile of this.zipFiles) {
+      const { filecontent, filename, base64 } = zipFile
+      zip.file(filename, filecontent, { base64 })
+    }
+    const blob = await zip.generateAsync({ type: 'blob' })
+    const anchor = this.doc.createElement('a')
+    anchor.href = URL.createObjectURL(blob)
+    anchor.download = this.zipFilename
+
+    this.doc.body.appendChild(anchor)
+    anchor.click()
+    this.doc.body.removeChild(anchor)
+    URL.revokeObjectURL(anchor.href)
+  }
+  constructor(
+    @Inject(DOCUMENT) private doc: Document
+  ){
+
+  }
+}
+
+export async function unzip(file: File): Promise<TZipFileConfig[]>{
+  const zip = new JSZip()
+  const loadedAsync = await zip.loadAsync(file)
+  
+  const out: TZipFileConfig[] = []
+  for (const filename in loadedAsync.files) {
+    const filecontent = await loadedAsync.files[filename].async('string')
+    out.push({
+      filename,
+      filecontent
+    })
+  }
+  return out
+}
diff --git a/typings/index.d.ts b/typings/index.d.ts
index 295f6b6846c87d1b3e7be1b46a8de83e8782459b..f61544924774ac285111e8c571eab5d4b659094a 100644
--- a/typings/index.d.ts
+++ b/typings/index.d.ts
@@ -14,6 +14,7 @@ declare var VERSION : string
 declare var PRODUCTION: boolean
 declare var BACKEND_URL: string
 declare var DATASET_PREVIEW_URL: string
+declare var BS_REST_URL: string
 declare var MATOMO_URL: string
 declare var MATOMO_ID: string
 declare var STRICT_LOCAL: boolean
diff --git a/webpack.aot.js b/webpack.aot.js
deleted file mode 100644
index f11f39b77e09aed04a563bfc245221c1c4b14491..0000000000000000000000000000000000000000
--- a/webpack.aot.js
+++ /dev/null
@@ -1,90 +0,0 @@
-const common = require('./webpack.common.js')
-const path = require('path')
-const ngtools = require('@ngtools/webpack')
-const HtmlWebpackPlugin = require('html-webpack-plugin')
-const AngularCompilerPlugin = ngtools.AngularCompilerPlugin
-const ClosureCompilerPlugin = require('webpack-closure-compiler')
-const merge = require('webpack-merge')
-const staticAssets = require('./webpack.staticassets')
-const TerserPlugin = require('terser-webpack-plugin')
-const webpack = require('webpack')
-
-module.exports = merge(staticAssets, {
-  mode: 'production',
-  entry : {
-    main : './src/main-aot.ts'
-  },
-  output : {
-    filename : '[name].js',
-    path : path.resolve(__dirname,'dist/aot')
-  },
-  module: {
-    rules: [
-      {
-        test : /third_party.*?\.js$|worker\.js/,
-        use : {
-          loader : 'file-loader',
-          options: {
-            name : '[name].[ext]'
-          }
-        }
-      },
-      {
-        test: /(?:\.ngfactory\.js|\.ngstyle\.js|\.ts)$/,
-        loader: '@ngtools/webpack',
-        exclude : /third_party|plugin_example|spec\.ts$/
-      },
-      {
-        test : /\.(html|css)$/,
-        exclude : /export\_nehuba|index|res\/css|plugin_example|material\/prebuilt-themes/,
-        use : {
-          loader : 'raw-loader',
-        }
-      },
-      {
-        test : /res\/css.*?css$/,
-        use : {
-          loader : 'file-loader',
-          options : {
-            name : '[name].[ext]'
-          }
-        }
-      }
-    ]
-  },
-  plugins : [
-    new HtmlWebpackPlugin({
-      template : 'src/index.html'
-    }),
-    new AngularCompilerPlugin({
-      tsConfigPath: 'tsconfig-aot.json',
-      entryModule: 'src/main.module#MainModule',
-      directTemplateLoading: true
-    }),
-    new webpack.DefinePlugin({
-      // TODO have to figure out how to set this properly
-      // needed to avoid inline eval
-      // shouldn't mode: 'production' do that already?
-      ngDevMode: false,
-      ngJitMode: false
-    })
-  ],
-  optimization: {
-    minimizer: [
-      new TerserPlugin({
-        extractComments: false
-      })
-    ]
-  },
-  resolve : {
-    extensions : [
-      '.ts',
-      '.js',
-      '.json'
-    ],
-    alias : {
-      "third_party" : path.resolve(__dirname,'third_party'),
-      "src" : path.resolve(__dirname,'src')
-    }
-  }
-})
\ No newline at end of file
diff --git a/webpack.dev-aot.js b/webpack.dev-aot.js
deleted file mode 100644
index 6ddd24edaa01173f6394865aa80b944a11bba63d..0000000000000000000000000000000000000000
--- a/webpack.dev-aot.js
+++ /dev/null
@@ -1,84 +0,0 @@
-const common = require('./webpack.common.js')
-const path = require('path')
-const ngtools = require('@ngtools/webpack')
-const HtmlWebpackPlugin = require('html-webpack-plugin')
-const AngularCompilerPlugin = ngtools.AngularCompilerPlugin
-const ClosureCompilerPlugin = require('webpack-closure-compiler')
-const merge = require('webpack-merge')
-const staticAssets = require('./webpack.staticassets')
-const TerserPlugin = require('terser-webpack-plugin')
-const webpack = require('webpack')
-
-module.exports = merge(staticAssets, {
-  mode: 'development',
-  entry : {
-    main : './src/main-aot.ts'
-  },
-  output : {
-    filename : '[name].js',
-    path : path.resolve(__dirname,'dist/aot')
-  },
-  devtool:'source-map',
-  module: {
-    rules: [
-      {
-        test : /third_party.*?\.js$|worker\.js/,
-        use : {
-          loader : 'file-loader',
-          options: {
-            name : '[name].[ext]'
-          }
-        }
-      },
-      {
-        test: /(?:\.ngfactory\.js|\.ngstyle\.js|\.ts)$/,
-        loader: '@ngtools/webpack',
-        exclude : /third_party|plugin_example/
-      },
-      {
-        test : /\.(html|css)$/,
-        exclude : /export\_nehuba|index|res\/css|plugin_example|material\/prebuilt-themes/,
-        use : {
-          loader : 'raw-loader',
-        }
-      },
-      {
-        test : /res\/css.*?css$/,
-        use : {
-          loader : 'file-loader',
-          options : {
-            name : '[name].[ext]'
-          }
-        }
-      }
-    ]
-  },
-  plugins : [
-    new HtmlWebpackPlugin({
-      template : 'src/index.html'
-    }),
-    new AngularCompilerPlugin({
-      tsConfigPath: 'tsconfig-aot.json',
-      entryModule: 'src/main.module#MainModule',
-      directTemplateLoading: true
-    }),
-    new webpack.DefinePlugin({
-      // TODO have to figure out how to set this properly
-      // needed to avoid inline eval
-      // shouldn't mode: 'production' do that already?
-      ngDevMode: false,
-      ngJitMode: false
-    })
-  ],
-  resolve : {
-    extensions : [
-      '.ts',
-      '.js',
-      '.json'
-    ],
-    alias : {
-      "third_party" : path.resolve(__dirname,'third_party'),
-      "src" : path.resolve(__dirname,'src')
-    }
-  }
-})
diff --git a/webpack/webpack.aot-common.js b/webpack/webpack.aot-common.js
new file mode 100644
index 0000000000000000000000000000000000000000..6c98c2f7cec402bdf5868eeaeec3db463ca331c8
--- /dev/null
+++ b/webpack/webpack.aot-common.js
@@ -0,0 +1,135 @@
+const webpack = require('webpack')
+const path = require('path')
+const HtmlWebpackPlugin = require('html-webpack-plugin')
+const ngtools = require('@ngtools/webpack')
+const AngularCompilerPlugin = ngtools.AngularCompilerPlugin
+const merge = require('webpack-merge')
+const staticAssets = require('./webpack.staticassets')
+
+const compileQuickOnePager = async () => {
+
+  const TITLE = 'Interactive Atlas Viewer Quickstart'
+
+  const showdown = require('showdown')
+  
+  const fs = require('fs')
+  const { promisify } = require('util')
+  const asyncReadfile = promisify(fs.readFile)
+  
+  const mdConverter = new showdown.Converter({
+    tables: true
+  })
+  
+  const pathToMd = path.join(__dirname, '../common/helpOnePager.md')
+  const mdData = await asyncReadfile(pathToMd, 'utf-8')
+  return `
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
+  <script src="https://unpkg.com/dompurify@latest/dist/purify.min.js"></script>
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>${TITLE}</title>
+</head>
+<body class="p-4">
+  
+</body>
+<script>
+(() => {
+  const dirty = \`${mdConverter.makeHtml(mdData).replace(/\`/g, '\\`')}\`
+  const clean = DOMPurify.sanitize(dirty)
+  document.body.innerHTML = clean
+})()
+</script>
+</html>
+`
+}
+const outputPath = path.resolve(__dirname,'../dist/aot')
+
+compileQuickOnePager()
+  .then(html => {
+    const fs = require('fs')
+    const { execSync } = require('child_process')
+    const { promisify } = require('util')
+    const asyncWrite = promisify(fs.writeFile)
+    execSync(`mkdir -p ${outputPath}`)
+    return asyncWrite(path.join(outputPath, 'quickstart.html'), html, 'utf-8')
+  })
+  .catch(e => {
+    console.warn(e)
+  })
+
+const commonAot = {
+  entry: {
+    main: './src/main-aot.ts'
+  },
+  output : {
+    filename : '[name].js',
+    path : outputPath
+  },
+  module: {
+    rules: [
+      {
+        test : /third_party.*?\.js$|worker\.js|worker-\w+\.js/,
+        use : {
+          loader : 'file-loader',
+          options: {
+            name : '[name].[ext]'
+          }
+        }
+      },
+      {
+        test: /(?:\.ngfactory\.js|\.ngstyle\.js|\.ts)$/,
+        loader: '@ngtools/webpack',
+        exclude : /third_party|plugin_example|spec\.ts|test\.ts/
+      },
+      {
+        test : /\.(html|css)$/,
+        exclude : /export\_nehuba|index|res\/css|plugin_example|material\/prebuilt-themes/,
+        use : {
+          loader : 'raw-loader',
+        }
+      },
+      {
+        test : /res\/css.*?css$/,
+        use : {
+          loader : 'file-loader',
+          options : {
+            name : '[name].[ext]'
+          }
+        }
+      }
+    ]
+  },
+  plugins : [
+    new HtmlWebpackPlugin({
+      template : 'src/index.html'
+    }),
+    new AngularCompilerPlugin({
+      tsConfigPath: 'tsconfig-aot.json',
+      entryModule: 'src/main.module#MainModule',
+      directTemplateLoading: true
+    }),
+    new webpack.DefinePlugin({
+      // TODO have to figure out how to set this properly
+      // needed to avoid inline eval
+      // shouldn't mode: 'production' do that already?
+      ngDevMode: false,
+      ngJitMode: false
+    })
+  ],
+  resolve : {
+    extensions : [
+      '.ts',
+      '.js',
+      '.json'
+    ],
+    alias : {
+      "third_party" : path.resolve(__dirname, '../third_party'),
+      "src" : path.resolve(__dirname, '../src')
+    }
+  }
+}
+
+module.exports = merge(staticAssets, commonAot)
\ No newline at end of file
diff --git a/webpack/webpack.aot.js b/webpack/webpack.aot.js
new file mode 100644
index 0000000000000000000000000000000000000000..0660ec650c1501aea74a41d9e617bb4fc2d9f005
--- /dev/null
+++ b/webpack/webpack.aot.js
@@ -0,0 +1,6 @@
+const merge = require('webpack-merge')
+const aotCommon = require('./webpack.aot-common')
+
+module.exports = merge(aotCommon, {
+  mode: 'production',
+})
diff --git a/webpack.common.js b/webpack/webpack.common.js
similarity index 52%
rename from webpack.common.js
rename to webpack/webpack.common.js
index 52e987bddbf7f90d2b16f55ff9b4a16b0b221953..1896b962e2e3a588987c31ddaaf02a997693859d 100644
--- a/webpack.common.js
+++ b/webpack/webpack.common.js
@@ -9,19 +9,10 @@ module.exports = {
         loaders : ['ts-loader','angular2-template-loader?keepUrl=true'],
         exclude : /node_modules|[Ss]pec\.ts$/
       },
-      {
-        test : /third_party|.*?worker.*?\.js$/,
-        use : {
-          loader : 'file-loader',
-          options: {
-            name : '[name].[ext]'
-          }
-        }
-      }
     ]
   },
   plugins : [
-    new webpack.ContextReplacementPlugin(/@angular(\\|\/)core(\\|\/)/,path.join(__dirname,'src'))
+    new webpack.ContextReplacementPlugin(/@angular(\\|\/)core(\\|\/)/,path.join(__dirname, '../src'))
   ],
   resolve : {
     extensions : [
@@ -30,10 +21,10 @@ module.exports = {
       '.json'
     ],
     alias : {
-      "third_party" : path.resolve(__dirname,'third_party'),
-      "src" : path.resolve(__dirname,'src'),
-      "common": path.resolve(__dirname, 'common'),
-      "spec": path.resolve(__dirname, 'spec')
+      "third_party" : path.resolve(__dirname, '../third_party'),
+      "src" : path.resolve(__dirname, '../src'),
+      "common": path.resolve(__dirname, '../common'),
+      "spec": path.resolve(__dirname, '../spec')
     }
   },
 }
\ No newline at end of file
diff --git a/webpack/webpack.dev-aot.js b/webpack/webpack.dev-aot.js
new file mode 100644
index 0000000000000000000000000000000000000000..cca9f12439dd89e528541f7e5cf2edb764df26b1
--- /dev/null
+++ b/webpack/webpack.dev-aot.js
@@ -0,0 +1,11 @@
+const merge = require('webpack-merge')
+const aotCommon = require('./webpack.aot-common')
+const path = require('path')
+
+module.exports = merge(aotCommon, {
+  mode: 'development',
+  devtool:'source-map',
+  devServer: {
+    contentBase: path.join(__dirname, '../dist/aot/')
+  }
+})
diff --git a/webpack.dev.js b/webpack/webpack.dev.js
similarity index 92%
rename from webpack.dev.js
rename to webpack/webpack.dev.js
index d32be20fe85174ca5ae969f2d1b99ca43d87558b..2a4fcf203ea57027ecfc6f8c5e0b3ccdb58453cd 100644
--- a/webpack.dev.js
+++ b/webpack/webpack.dev.js
@@ -12,7 +12,7 @@ module.exports = merge(common,ngAssets,staticAssets,{
   mode : 'development',
   output : {
     filename : '[name].js',
-    path : path.resolve(__dirname,'dist/dev')
+    path : path.resolve(__dirname,'../dist/dev')
   },
   devtool:'source-map',
 
diff --git a/webpack.export.aot.js b/webpack/webpack.export.aot.js
similarity index 94%
rename from webpack.export.aot.js
rename to webpack/webpack.export.aot.js
index fda5c27b38cc2f8a9208ea0db0b72110359510ab..2612d09449efa54b3a1e4f3bbbd16aceb4744c4b 100644
--- a/webpack.export.aot.js
+++ b/webpack/webpack.export.aot.js
@@ -10,7 +10,7 @@ module.exports = {
   entry : './src/main-aot.ts',
   output : {
     filename : 'main.js',
-    path : path.resolve(__dirname,'dist/export-aot')
+    path : path.resolve(__dirname,'../dist/export-aot')
   },
   module: {
     rules: [
diff --git a/webpack.export.js b/webpack/webpack.export.js
similarity index 93%
rename from webpack.export.js
rename to webpack/webpack.export.js
index d6cc210c905ec7c59a9a8dd123019e407e3a69c5..1276bc1b5a12de4d22e98f5f17ffed32c6a38c25 100644
--- a/webpack.export.js
+++ b/webpack/webpack.export.js
@@ -10,7 +10,7 @@ module.exports = merge(common,ngAssets,{
   mode : 'development',
   output : {
     filename : 'export.js',
-    path : path.resolve(__dirname,'dist/export')
+    path : path.resolve(__dirname,'../dist/export')
   },
   devtool:'source-map',
   plugins : [
diff --git a/webpack.export.min.js b/webpack/webpack.export.min.js
similarity index 92%
rename from webpack.export.min.js
rename to webpack/webpack.export.min.js
index eff53e1cc79c6f71a3a5eb7ce80ba546cdaa02c9..deefdbceeb145418c988df256b4fb85606dd4e7a 100644
--- a/webpack.export.min.js
+++ b/webpack/webpack.export.min.js
@@ -9,7 +9,7 @@ module.exports = merge(common,ngAssets,{
   entry : './src/atlasViewerExports/main.export.ts',
   output : {
     filename : 'export.js',
-    path : path.resolve(__dirname,'dist/export-min')
+    path : path.resolve(__dirname,'../dist/export-min')
   },
   plugins : [
     new ClosureCompilerPlugin({
diff --git a/webpack.ngassets.js b/webpack/webpack.ngassets.js
similarity index 100%
rename from webpack.ngassets.js
rename to webpack/webpack.ngassets.js
diff --git a/webpack.prod.js b/webpack/webpack.prod.js
similarity index 93%
rename from webpack.prod.js
rename to webpack/webpack.prod.js
index 1119c6948c84f0b87eeb626acd7a1b4ba779f97d..325cdd3de8dfd4631952fe887994ab13bd5000f5 100644
--- a/webpack.prod.js
+++ b/webpack/webpack.prod.js
@@ -11,7 +11,7 @@ module.exports = merge(common,ngAssets,staticAssets,{
   entry : './src/main.ts',
   output : {
     filename : 'main.js',
-    path : path.resolve(__dirname,'dist/prod')
+    path : path.resolve(__dirname,'../dist/prod')
   },
   plugins : [
     new ClosureCompilerPlugin({
diff --git a/webpack.staticassets.js b/webpack/webpack.staticassets.js
similarity index 92%
rename from webpack.staticassets.js
rename to webpack/webpack.staticassets.js
index 2b23b8b46f6546c6bad8a1c148e0c135780ed1b6..a5d7ef3eeb12982fcb10716bf747027327e11e1b 100644
--- a/webpack.staticassets.js
+++ b/webpack/webpack.staticassets.js
@@ -59,14 +59,15 @@ module.exports = {
     }),
     new webpack.DefinePlugin({
 
-      VERSION: process.env.VERSION 
-        ? JSON.stringify(process.env.VERSION) 
+      VERSION: process.env.VERSION
+        ? JSON.stringify(process.env.VERSION)
         : process.env.GIT_HASH
           ? JSON.stringify(process.env.GIT_HASH)
           : JSON.stringify('unspecificied hash'),
       PRODUCTION: !!process.env.PRODUCTION,
       BACKEND_URL: (process.env.BACKEND_URL && JSON.stringify(process.env.BACKEND_URL)) || 'null',
       DATASET_PREVIEW_URL: JSON.stringify(process.env.DATASET_PREVIEW_URL || 'https://hbp-kg-dataset-previewer.apps.hbp.eu/v2'),
+      BS_REST_URL: JSON.stringify(process.env.BS_REST_URL || 'https://siibra-api-latest.apps-dev.hbp.eu/v1_0'),
       SPATIAL_TRANSFORM_BACKEND: JSON.stringify(process.env.SPATIAL_TRANSFORM_BACKEND || 'https://hbp-spatial-backend.apps.hbp.eu'),
       MATOMO_URL: JSON.stringify(process.env.MATOMO_URL || null),
       MATOMO_ID: JSON.stringify(process.env.MATOMO_ID || null),
@@ -83,4 +84,4 @@ module.exports = {
       '.scss'
     ]
   }
-}
\ No newline at end of file
+}
diff --git a/webpack.test.js b/webpack/webpack.test.js
similarity index 100%
rename from webpack.test.js
rename to webpack/webpack.test.js
diff --git a/worker/worker-plotly.js b/worker/worker-plotly.js
new file mode 100644
index 0000000000000000000000000000000000000000..505cb1e630b2bccd04a1d6f0578972a7465c020b
--- /dev/null
+++ b/worker/worker-plotly.js
@@ -0,0 +1,196 @@
+(function(exports){
+  /**
+   * units in mm --> convert to nm
+   */
+  const plotyMultiple = 1e6
+  const { vtkHeader } = globalThis.constants || {}
+
+  const getPerpendicularPointsForLine = (A, B, scale) => {
+
+    const lineWeight = 1e6
+    const lineWidth = scale * lineWeight
+    const lineHeight = scale * lineWeight
+  
+    let u = A.map((item, index) => {
+      return item - B[index];
+    })
+    const uLength = Math.sqrt((u[0] * u[0]) + (u[1] * u[1]) + (u[2] * u[2]))
+    u = u.map((item, index) => {
+      return item/uLength
+    })
+  
+    const n = []
+    if(Math.abs(u[0]) <= Math.abs(u[1]) && Math.abs(u[0]) <= Math.abs(u[2])) {
+      n[0] = u[1] * u[1] + u[2] * u[2]
+      n[1] = -u[1] * u[0]
+      n[2] = -u[2] * u[0]
+    }
+    else if(Math.abs(u[1])<=Math.abs(u[0])&&Math.abs(u[1])<=Math.abs(u[2]))
+    {
+      n[0] = -u[0] * u[2]
+      n[1] = u[0] * u[0] + u[2] * u[2]
+      n[2] = -u[2] * u[1]
+    }
+    else if(Math.abs(u[2])<=Math.abs(u[0])&&Math.abs(u[2])<=Math.abs(u[1]))
+    {
+      n[0] = -u[0] * u[2]
+      n[1] = -u[1] * u[2]
+      n[2] = u[0] * u[0] + u[1] * u[1]
+    }
+  
+    const v = [ u[1] * n[2] - u[2] * n[1], u[2] * n[0] - u[0] * n[2], u[0] * n[1] - u[1] * n[0] ]
+  
+    const RMul = (k) => {
+      const res = []
+      res[0] = v[0]*k[0] + n[0]*k[1] + u[0]*k[2]
+      res[1] = v[1]*k[0] + n[1]*k[1] + u[1]*k[2]
+      res[2] = v[2]*k[0] + n[2]*k[1] + u[2]*k[2]
+      return res
+    }
+  
+    const sumArrays = (a1, a2) => {
+      return a1.map((item, index) => {
+        return item + a2[index];
+      })
+    }
+  
+    const a = sumArrays(A, RMul([lineWidth,lineHeight,0]))
+    const b = sumArrays(A, RMul([-lineWidth,lineHeight,0]))
+    const c = sumArrays(A, RMul([lineWidth,-lineHeight,0]))
+    const d = sumArrays(A, RMul([-lineWidth,-lineHeight,0]))
+  
+    const e = sumArrays(B, RMul([lineWidth,lineHeight,0]))
+    const f = sumArrays(B, RMul([-lineWidth,lineHeight,0]))
+    const g = sumArrays(B, RMul([lineWidth,-lineHeight,0]))
+    const h = sumArrays(B, RMul([-lineWidth,-lineHeight,0]))
+  
+    return `${a.join(' ')}\n ${b.join(' ')}\n ${c.join(' ')}\n ${d.join(' ')}\n ${e.join(' ')}\n ${f.join(' ')}\n ${g.join(' ')}\n ${h.join(' ')}\n `
+  }
+
+  const getFragmentColorString = (colors) => {
+  
+    const hexToRgb = (hex) => {
+      const [r, g, b] = hex.match(/\w\w/g).map(x => parseInt(x, 16))
+      return `emitRGB(vec3(${r/255}, ${g/255}, ${b/255}))`
+    }
+  
+    const colorsUnique = colors.filter((cf, i) => colors[i-1] !== cf)
+      .map((color, j) => {
+        return `if (label > ${j - 0.01} && label < ${j + 0.01}) { ${hexToRgb(color)}; }`
+      })
+  
+    const fragmentColorString = `${colorsUnique.join(' else ')} else {emitRGB(vec3(1.0, 0.1, 0.12));}`
+    return fragmentColorString
+  }
+
+  const getLineDataVtkPolygonStringWithNumber = (neuronCoordinateLength) => {
+    let returnString = ''
+    for (let i = 0; i < neuronCoordinateLength; i++) {
+      const neuronNumber = 8*i
+      returnString +=
+        `3 ${0 + neuronNumber} ${1 + neuronNumber} ${3 + neuronNumber}\n` +
+        `3 ${0 + neuronNumber} ${2 + neuronNumber} ${3 + neuronNumber}\n` +
+        `3 ${4 + neuronNumber} ${5 + neuronNumber} ${7 + neuronNumber}\n` +
+        `3 ${4 + neuronNumber} ${6 + neuronNumber} ${7 + neuronNumber}\n` +
+        `3 ${2 + neuronNumber} ${6 + neuronNumber} ${7 + neuronNumber}\n` +
+        `3 ${2 + neuronNumber} ${3 + neuronNumber} ${7 + neuronNumber}\n` +
+        `3 ${3 + neuronNumber} ${1 + neuronNumber} ${7 + neuronNumber}\n` +
+        `3 ${1 + neuronNumber} ${5 + neuronNumber} ${7 + neuronNumber}\n` +
+        `3 ${2 + neuronNumber} ${0 + neuronNumber} ${6 + neuronNumber}\n` +
+        `3 ${0 + neuronNumber} ${4 + neuronNumber} ${6 + neuronNumber}\n` +
+        `3 ${1 + neuronNumber} ${0 + neuronNumber} ${4 + neuronNumber}\n` +
+        `3 ${1 + neuronNumber} ${4 + neuronNumber} ${5 + neuronNumber}\n`
+    }
+    return returnString
+  }
+
+  const getColorIds = (colors) => {
+    let returnString = ''
+  
+    let colorId = 0
+  
+    for (let i=0; i < colors.length; i++){
+      if (i > 0 && colors[i] !== colors[i-1]) {
+        colorId += 1
+      }
+      for (let j=0; j < 8; j++){
+        if (i === colors.length-1 && j === 7) {
+          returnString += colorId
+        } else {
+          returnString += colorId + '\n'
+        }
+      }
+    }
+  
+    return returnString
+  }
+  
+  const parseLineDataToVtk = (data, scale= 1, plotyMultiple) => {
+    const lineCoordinates = []
+    const colors = []
+
+    for (let i = 1; i < data.x.length; i++) {
+
+      if (data.x[i] !== null && data.x[i-1] !== null) {
+        lineCoordinates.push([[
+          data.x[i-1] * plotyMultiple,
+          data.y[i-1] * plotyMultiple,
+          data.z[i-1] * plotyMultiple,
+        ], [
+          data.x[i] * plotyMultiple,
+          data.y[i] * plotyMultiple,
+          data.z[i] * plotyMultiple,
+        ]])
+
+        colors.push(data.marker.color[i-1])
+      }
+    }
+
+    const coordinateLength = lineCoordinates.length
+
+    const lineCoordinatesArrayToString = (() => {
+      let returnString = ''
+      lineCoordinates.forEach(lc => {
+        returnString += getPerpendicularPointsForLine(lc[0], lc[1], scale)
+      })
+      return returnString
+    })()
+
+    const customFragmentColor = getFragmentColorString(colors)
+
+    const vtkString = `${vtkHeader}\n` +
+      `POINTS ${coordinateLength*8} float\n` +
+      lineCoordinatesArrayToString +
+      `POLYGONS ${coordinateLength*12} ${coordinateLength*48}\n` +
+      getLineDataVtkPolygonStringWithNumber(coordinateLength) +
+      `POINT_DATA ${coordinateLength*8}\n` +
+      'SCALARS label unsigned_char 1\n' +
+      'LOOKUP_TABLE none\n' +
+      getColorIds(colors)
+
+    return {vtkString, customFragmentColor}
+  }
+
+  exports.plotly = {
+    plotyMultiple,
+    convert: plotlyData => {
+      const { x, y, z } = plotlyData.traces[0]
+      const lm = []
+      for (const idx in x) {
+        if (typeof x !== 'undefined' && x !== null) {
+          lm.push([x[idx]*plotyMultiple, y[idx]*plotyMultiple, z[idx]*plotyMultiple])
+        }
+      }
+      const { vtkString, customFragmentColor } = parseLineDataToVtk(plotlyData.traces[0], 5e-3, plotyMultiple)
+
+      return {
+        vtkString,
+        customFragmentColor,
+      }
+    }
+  }
+})(
+  typeof exports === 'undefined'
+  ? self
+  : exports
+)
diff --git a/worker/worker-plotly.spec.js b/worker/worker-plotly.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..c00f81922635b9eb71434841b61700acd28c5c34
--- /dev/null
+++ b/worker/worker-plotly.spec.js
@@ -0,0 +1,36 @@
+const vtkHeader = `# vtk DataFile Version 2.0
+Created by worker thread at https://github.com/HumanBrainProject/interactive-viewer
+ASCII
+DATASET POLYDATA`
+
+globalThis.constants = {
+  vtkHeader,
+}
+
+const { plotly } = require('./worker-plotly')
+
+const input = {
+  traces: [{
+    "type":"scatter3d",
+    "mode":"markers",
+    "name":"points",
+    "x":[-0.9557710289955139,null,-0.9557710289955139,-0.972806990146637,null],
+    "y":[4.165919780731201,null,4.165919780731201,4.160162925720215,null],
+    "z":[1.925400972366333,null,1.925400972366333,1.9260079860687256,null],"opacity":1,
+    "marker":{"size":1,"color":["#00dd00",null,"#ff0000","#ff0000",null],
+    "reversescale":false}
+  }]  
+}
+const expectedOutput = {
+  customFragmentColor: "if (label > -0.01 && label < 0.01) { emitRGB(vec3(1, 0, 0)); } else {emitRGB(vec3(1.0, 0.1, 0.12));}",
+  vtkString: "# vtk DataFile Version 2.0\nCreated by worker thread at https://github.com/HumanBrainProject/interactive-viewer\nASCII\nDATASET POLYDATA\nPOINTS 8 float\n-954011.5298146864 4161239.595879443 1930395.281492923\n -957211.0971719137 4170707.908889603 1930395.281492923\n -954330.9608191141 4161131.6525727995 1920406.663239743\n -957530.5281763414 4170599.9655829594 1920406.663239743\n -971047.4909658094 4155482.7408684567 1931002.2951953155\n -974247.0583230368 4164951.0538786165 1931002.2951953155\n -971366.9219702372 4155374.797561813 1921013.6769421357\n -974566.4893274645 4164843.110571973 1921013.6769421357\n POLYGONS 12 48\n3 0 1 3\n3 0 2 3\n3 4 5 7\n3 4 6 7\n3 2 6 7\n3 2 3 7\n3 3 1 7\n3 1 5 7\n3 2 0 6\n3 0 4 6\n3 1 0 4\n3 1 4 5\nPOINT_DATA 8\nSCALARS label unsigned_char 1\nLOOKUP_TABLE none\n0\n0\n0\n0\n0\n0\n0\n0"
+}
+
+describe('> worker-plotly.js', () => {
+  it('> expect input === output', () => {
+
+    expect(
+      plotly.convert(input)
+    ).toEqual(expectedOutput)
+  })
+})
diff --git a/src/util/worker.js b/worker/worker.js
similarity index 93%
rename from src/util/worker.js
rename to worker/worker.js
index 9ca462de1c0e246c9fa36df94b315c742d78d373..cd05c6ae4d954567cbf8411f2df2247269896065 100644
--- a/src/util/worker.js
+++ b/worker/worker.js
@@ -1,3 +1,19 @@
+const vtkHeader = `# vtk DataFile Version 2.0
+Created by worker thread at https://github.com/HumanBrainProject/interactive-viewer
+ASCII
+DATASET POLYDATA`
+
+globalThis.constants = {
+  vtkHeader
+}
+
+if (typeof self.importScripts === 'function')  self.importScripts('./worker-plotly.js')
+
+/**
+ * TODO migrate processing functionalities to other scripts
+ * see worker-plotly.js
+ */
+
 const validTypes = [
   'GET_LANDMARKS_VTK',
   'GET_USERLANDMARKS_VTK',
@@ -20,11 +36,6 @@ const validOutType = [
   'UPDATE_PARCELLATION_REGIONS'
 ]
 
-const vtkHeader = `# vtk DataFile Version 2.0
-Created by worker thread at https://github.com/HumanBrainProject/interactive-viewer
-ASCII
-DATASET POLYDATA`
-
 const getVertexHeader = (numVertex) => `POINTS ${numVertex} float`
 
 const getPolyHeader = (numPoly) => `POLYGONS ${numPoly} ${4 * numPoly}`
@@ -47,7 +58,7 @@ const getIcoVertex = (pos, scale) => `-525731.0 0.0 850651.0
 850651.0 -525731.0 0.0
 -850651.0 -525731.0 0.0`
   .split('\n')
-  .map(line => 
+  .map(line =>
     line
       .split(' ')
       .map((string, idx) => (Number(string) * (scale ? scale : 1) + pos[idx]).toString() )
@@ -77,7 +88,7 @@ const getIcoPoly = (startingIdx) => `3 1 4 0
 3 5 2 9
 3 11 2 7`
   .split('\n')
-  .map((line) => 
+  .map((line) =>
     line
       .split(' ')
       .map((v,idx) => idx === 0 ? v : (Number(v) + startingIdx).toString() )
@@ -86,8 +97,8 @@ const getIcoPoly = (startingIdx) => `3 1 4 0
   .join('\n')
 
 const getMeshVertex = (vertices) =>  vertices.map(vertex => vertex.join(' ')).join('\n')
-const getMeshPoly = (polyIndices, currentIdx) => polyIndices.map(triplet => 
-  '3 '.concat(triplet.map(index => 
+const getMeshPoly = (polyIndices, currentIdx) => polyIndices.map(triplet =>
+  '3 '.concat(triplet.map(index =>
     index + currentIdx
   ).join(' '))
 ).join('\n')
@@ -114,7 +125,7 @@ const parseLmToVtk = (landmarks, scale) => {
     else{
       //curr[0] : [number,number,number][] vertices
       //curr[1] : [number,number,number][] indices for the vertices that poly forms
-      
+
       /**
        * poly primitive
        */
@@ -137,7 +148,7 @@ const parseLmToVtk = (landmarks, scale) => {
     labelString : [],
   })
 
-  // if no vertices are been rendered, do not replace old 
+  // if no vertices are been rendered, do not replace old
   if(reduce.currentVertexIndex === 0)
     return false
 
@@ -168,7 +179,7 @@ const getLandmarksVtk = (action) => {
     : 2.8
 
   const vtk = parseLmToVtk(landmarks, scale)
-  
+
   if(!vtk) return
 
   // when new set of landmarks are to be displayed, the old landmarks will be discarded
@@ -228,7 +239,7 @@ const rebuildSelectedRegion = (payload) => {
   const activeTreeBranch = []
   const isRegionActive = (region) => selectedRegions.some(r => r.name === region.name)
     || region.children && region.children.length > 0 && region.children.every(isRegionActive)
-  
+
   /**
    * some active tree branch
    * branch is active if SOME children are active
@@ -240,7 +251,7 @@ const rebuildSelectedRegion = (payload) => {
   const handleRegion = (r) => {
     isRegionActive(r) ? activeTreeBranch.push(r) : {}
     isSomeRegionActive(r) ? someActiveTreeBranch.push(r) : {}
-    if (r.children && r.children.length > 0) 
+    if (r.children && r.children.length > 0)
       r.children.forEach(handleRegion)
   }
   regions.forEach(handleRegion)
@@ -295,31 +306,25 @@ const processParcRegionAttr = (payload) => {
 let plotyVtkUrl
 
 onmessage = (message) => {
+
   if (message.data.method && VALID_METHODS.indexOf(message.data.method) >= 0) {
     const { id } = message.data
     if (message.data.method === VALID_METHOD.PROCESS_PLOTLY) {
-      /**
-       * units in mm --> convert to nm
-       */
-      const plotyMultiple=1e6
       try {
-        const { data: plotlyData } = message.data.param
-        const { x, y, z } = plotlyData.traces[0]
-        const lm = []
-        for (const idx in x) {
-          if (typeof x !== 'undefined' && x !== null) {
-            lm.push([x[idx]*plotyMultiple, y[idx]*plotyMultiple, z[idx]*plotyMultiple])
-          }
-        }
         if (plotyVtkUrl) URL.revokeObjectURL(plotyVtkUrl)
-        const vtkString = parseLmToVtk(lm, 3e-2)
-        plotyVtkUrl = URL.createObjectURL(
+        const { data: plotlyData } = message.data.param
+        const {
+          vtkString,
+          customFragmentColor
+        } = self.plotly.convert(plotlyData)
+        const plotyVtkUrl = URL.createObjectURL(
           new Blob([ encoder.encode(vtkString) ], { type: 'application/octet-stream' })
         )
         postMessage({
           id,
           result: {
-            objectUrl: plotyVtkUrl
+            objectUrl: plotyVtkUrl,
+            customFragmentColor
           }
         })
       } catch (e) {
@@ -341,7 +346,7 @@ onmessage = (message) => {
     })
     return
   }
-  
+
   if(validTypes.findIndex(type => type === message.data.type) >= 0){
     switch(message.data.type){
       case 'GET_LANDMARKS_VTK':