diff --git a/e2e/src/navigating/changeTemplate.e2e-spec.js b/e2e/src/navigating/changeTemplate.e2e-spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..a1585c415462e383540698f8dd349f032fff3e9d
--- /dev/null
+++ b/e2e/src/navigating/changeTemplate.e2e-spec.js
@@ -0,0 +1,33 @@
+const { AtlasPage } = require('../util')
+
+describe('trans template navigation', () => {
+  let iavPage
+  
+  beforeEach(async () => {
+    iavPage = new AtlasPage()
+    await iavPage.init()
+    
+  })
+
+  it('when user moves from template a -> template b, a xhr call is made', async () => {
+
+    const searchParam = new URLSearchParams()
+    searchParam.set('templateSelected', 'MNI 152 ICBM 2009c Nonlinear Asymmetric')
+    searchParam.set('parcellationSelected', 'JuBrain Cytoarchitectonic Atlas')
+    await iavPage.goto(`/?${searchParam.toString()}`, { interceptHttp: true, doNotAutomate: true })
+    await iavPage.wait(200)
+    await iavPage.dismissModal()
+    await iavPage.waitUntilAllChunksLoaded()
+
+    await iavPage.selectDropdownTemplate('Big Brain (Histology)')
+    await iavPage.wait(2000)
+    const interceptedCalls = await iavPage.getInterceptedHttpCalls()
+
+    const found = interceptedCalls.find(({ method, url }) => {
+      return method === 'POST' && /transform-points/.test(url)
+    })
+    expect(!!found).toBe(true)
+  })
+
+  // TODO test that nav/zoom/orientation are actually preserved
+})
diff --git a/e2e/src/util.js b/e2e/src/util.js
index b43524d943ba41018329e023a37d55450f0d7f5a..88cc67bcd24e1356f8a8fc252ff76716a67ba598 100644
--- a/e2e/src/util.js
+++ b/e2e/src/util.js
@@ -92,6 +92,8 @@ class WdBase{
       return window['__interceptedXhr__']
     })
   }
+
+  // it seems if you set intercept http to be true, you might also want ot set do not automat to be true
   async goto(url = '/', { interceptHttp, doNotAutomate } = {}){
     const actualUrl = getActualUrl(url)
     if (interceptHttp) {
diff --git a/src/services/effect/effect.spec.ts b/src/services/effect/effect.spec.ts
index bf8dab818ed67a8852ee2f055dffd2afdb3c208c..c8bb115fca5e4d06deba3e657583e2443f6a52e7 100644
--- a/src/services/effect/effect.spec.ts
+++ b/src/services/effect/effect.spec.ts
@@ -55,14 +55,10 @@ describe('effect.ts', () => {
           provideMockStore({ initialState: defaultRootState })
         ]
       })
-
-      const useEffectsInstance: UseEffects = TestBed.get(UseEffects)
-      useEffectsInstance.onParcellationSelected$.subscribe(console.log)
-
     })
 
     it('both SELECT_PARCELLATION and NEWVIEWER actions should trigger onParcellationSelected$', () => {
-      const useEffectsInstance: UseEffects = TestBed.get(UseEffects)
+      const useEffectsInstance: UseEffects = TestBed.inject(UseEffects)
       actions$ = hot(
         'ab',
         {
diff --git a/src/services/effect/effect.ts b/src/services/effect/effect.ts
index 313663300fc99e13f3ae79486c49dc9da17b114a..16f5a3b1e9aebd4a41030d888c5aa829083a730f 100644
--- a/src/services/effect/effect.ts
+++ b/src/services/effect/effect.ts
@@ -4,7 +4,7 @@ import { select, Store } from "@ngrx/store";
 import { merge, Observable, Subscription } from "rxjs";
 import { filter, map, shareReplay, switchMap, take, withLatestFrom, mapTo } from "rxjs/operators";
 import { LoggingService } from "../logging.service";
-import { ADD_TO_REGIONS_SELECTION_WITH_IDS, DESELECT_REGIONS, NEWVIEWER, SELECT_PARCELLATION, SELECT_REGIONS, SELECT_REGIONS_WITH_ID } from "../state/viewerState.store";
+import { ADD_TO_REGIONS_SELECTION_WITH_IDS, DESELECT_REGIONS, NEWVIEWER, CHANGE_NAVIGATION, SELECT_PARCELLATION, SELECT_REGIONS, SELECT_REGIONS_WITH_ID, SELECT_LANDMARKS } from "../state/viewerState.store";
 import { generateLabelIndexId, getNgIdLabelIndexFromId, IavRootStoreInterface, recursiveFindRegionWithLabelIndexId, getMultiNgIdsRegionsLabelIndexMap, GENERAL_ACTION_TYPES } from '../stateStore.service';
 
 @Injectable({
@@ -189,6 +189,29 @@ export class UseEffects implements OnDestroy {
       selectRegions: [],
     })
   )
+
+  /**
+   * side effects of loading a new template space
+   * reset navigation (navigation translation is done by a different service)
+   * Landmarks will no longer be accurate (differente template space)
+   */
+  @Effect()
+  public onNewViewerResetNavigation$ = this.actions$.pipe(
+    ofType(NEWVIEWER),
+    mapTo({
+      type: CHANGE_NAVIGATION,
+      navigation: {}
+    })
+  )
+
+  @Effect()
+  public onNewViewerResetLandmarkSelected$ = this.actions$.pipe(
+    ofType(NEWVIEWER),
+    mapTo({
+      type: SELECT_LANDMARKS,
+      landmarks: []
+    })
+  )
 }
 
 export const getGetRegionFromLabelIndexId = ({ parcellation }) => {
diff --git a/src/services/state/viewerState.store.ts b/src/services/state/viewerState.store.ts
index 4310e2185d1af89421ee4711e0cc3ba847a79fbe..6c0ac15d27a0017143f0b5220e94f3e2f47850e0 100644
--- a/src/services/state/viewerState.store.ts
+++ b/src/services/state/viewerState.store.ts
@@ -94,7 +94,9 @@ export const getStateStore = ({ state = defaultState } = {}) => (prevState: Part
       parcellationSelected : parcellation,
       // taken care of by effect.ts
       // regionsSelected : [],
-      landmarksSelected : [],
+
+      // taken care of by effect.ts
+      // landmarksSelected : [],
       // navigation : {},
       dedicatedView : null,
     }
diff --git a/src/services/templateCoordinatesTransformation.service.spec.ts b/src/services/templateCoordinatesTransformation.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d91b6f89f3ad176527f1fcad20043f012ade1a97
--- /dev/null
+++ b/src/services/templateCoordinatesTransformation.service.spec.ts
@@ -0,0 +1,109 @@
+import { TemplateCoordinatesTransformation } from './templateCoordinatesTransformation.service'
+import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'
+import { TestBed, fakeAsync, tick } from '@angular/core/testing'
+
+describe('templateCoordinatesTransformation.service.spec.ts', () => {
+  describe('TemplateCoordinatesTransformation', () => {
+    beforeEach(() => {
+      TestBed.configureTestingModule({
+        imports: [
+          HttpClientTestingModule
+        ],
+        providers: [
+          TemplateCoordinatesTransformation
+        ]
+      })
+    })
+
+    afterEach(() => {
+      const ctrl = TestBed.inject(HttpTestingController)
+      ctrl.verify()
+    })
+
+    describe('getPointCoordinatesForTemplate', () => {
+      it('should instantiate service properly', () => {
+        const service = TestBed.inject(TemplateCoordinatesTransformation)
+        expect(service).toBeTruthy()
+        expect(service.getPointCoordinatesForTemplate).toBeTruthy()
+      })
+
+      it('should transform argument properly', () => {
+        const service = TestBed.inject(TemplateCoordinatesTransformation)
+        const httpTestingController = TestBed.inject(HttpTestingController)
+
+        service.getPointCoordinatesForTemplate(
+          'from',
+          'target',
+          [1,2,3]
+        )
+        const req = httpTestingController.expectOne(service.url)
+        expect(req.request.method).toEqual('POST')
+        expect(
+          JSON.parse(req.request.body)
+        ).toEqual({
+          'source_space': 'from',
+          'target_space': 'target',
+          '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',
+          [1,2,3]
+        ).subscribe(({ status, result }) => {
+          expect(status).toEqual('completed')
+          expect(result).toEqual([1e6, 2e6, 3e6])
+        })
+        const req = httpTestingController.expectOne(service.url)
+        req.flush({
+          'target_points':[
+            [1, 2, 3]
+          ]
+        })
+      })
+
+      it('if server returns >=400, fallback gracefully', () => {
+        const service = TestBed.inject(TemplateCoordinatesTransformation)
+        const httpTestingController = TestBed.inject(HttpTestingController)
+
+        service.getPointCoordinatesForTemplate(
+          'from',
+          'target',
+          [1,2,3]
+        ).subscribe(({ status }) => {
+          expect(status).toEqual('error')
+        })
+        const req = httpTestingController.expectOne(service.url)
+        
+        req.flush('intercepted', { status: 500, statusText: 'internal server error' })
+      })
+
+      it('if server does not respond after 3s, fallback gracefully', fakeAsync(() => {
+
+        const service = TestBed.inject(TemplateCoordinatesTransformation)
+        const httpTestingController = TestBed.inject(HttpTestingController)
+
+        service.getPointCoordinatesForTemplate(
+          'from',
+          'target',
+          [1,2,3]
+        ).subscribe(({ status, statusText }) => {
+          expect(status).toEqual('error')
+          expect(statusText).toEqual(`Timeout after 3s`)
+        })
+        const req = httpTestingController.expectOne(service.url)
+        tick(4000)
+        expect(req.cancelled).toBe(true)
+      }))
+    })
+  })
+})
diff --git a/src/services/templateCoordinatesTransformation.service.ts b/src/services/templateCoordinatesTransformation.service.ts
index 5eb89e6eafe31608fbe96b6a20745805bc31fc9b..b226aadee2b89ad99c9b3f8ccf5c936894eb5ea7 100644
--- a/src/services/templateCoordinatesTransformation.service.ts
+++ b/src/services/templateCoordinatesTransformation.service.ts
@@ -1,5 +1,13 @@
 import {Injectable} from "@angular/core";
-import {HttpClient, HttpHeaders} from "@angular/common/http";
+import {HttpClient, HttpHeaders, HttpErrorResponse} from "@angular/common/http";
+import { catchError, timeout, map } from "rxjs/operators";
+import { of, Observable } from "rxjs";
+
+export interface ITemplateCoordXformResp{
+  status: 'pending' | 'error' | 'completed',
+  statusText?: string,
+  result? : [number, number, number]
+}
 
 @Injectable({
   providedIn: 'root',
@@ -8,39 +16,41 @@ export class TemplateCoordinatesTransformation {
 
   constructor(private httpClient: HttpClient) {}
 
-  getPointCoordinatesForTemplate(sourceTemplateName, targetTemplateName, coordinates) {
-    const url = 'https://hbp-spatial-backend.apps-dev.hbp.eu/v1/transform-points'
+  public url = 'https://hbp-spatial-backend.apps-dev.hbp.eu/v1/transform-points'
+
+  // jasmine marble cannot test promise properly
+  // see https://github.com/ngrx/platform/issues/498#issuecomment-337465179
+  // in order to properly test with marble, use obs instead of promise
+  getPointCoordinatesForTemplate(sourceTemplateName: string, targetTemplateName: string, coordinatesInNm: [number, number, number]): Observable<ITemplateCoordXformResp> {
     const httpOptions = {
       headers: new HttpHeaders({
         'Content-Type':  'application/json'
       })
     }
-
-    const convertedPoints = new Promise((resolve, reject) => {
-      let timeOut = true
-      setTimeout(() => {
-        if (timeOut) reject('Timed out')
-      },3000)
-
-      this.httpClient.post(
-        url,
-        JSON.stringify({
-          'source_points': [[...coordinates.map(c => c/1000000)]],
-          'source_space': sourceTemplateName,
-          'target_space': targetTemplateName
-        }),
-        httpOptions
-      ).toPromise().then(
-        res => {
-          timeOut = false
-          resolve(res['target_points'][0].map(r=> r*1000000))
-        },
-        msg => {
-          timeOut = false
-          reject(msg)
+    return this.httpClient.post(
+      this.url,
+      JSON.stringify({
+        'source_points': [[...coordinatesInNm.map(c => c/1e6)]],
+        'source_space': sourceTemplateName,
+        'target_space': targetTemplateName
+      }),
+      httpOptions
+    ).pipe(
+      map(resp => {
+        return {
+          status: 'completed',
+          result: resp['target_points'][0].map((r:number)=> r * 1e6)
+        } as ITemplateCoordXformResp
+      }),
+      catchError(err => {
+        if (err instanceof HttpErrorResponse) {
+          return of(({ status: 'error', statusText: err.message } as ITemplateCoordXformResp))
+        } else {
+          return of(({ status: 'error', statusText: err.toString() } as ITemplateCoordXformResp))
         }
-      )
-    })
-    return convertedPoints
+      }),
+      timeout(3000),
+      catchError(() => of(({ status: 'error', statusText: `Timeout after 3s` } as ITemplateCoordXformResp))),
+    )
   }
 }
\ No newline at end of file
diff --git a/src/ui/viewerStateController/viewerState.useEffect.spec.ts b/src/ui/viewerStateController/viewerState.useEffect.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..36e9b115bb723f465e0163338447ea434f9cc2ac
--- /dev/null
+++ b/src/ui/viewerStateController/viewerState.useEffect.spec.ts
@@ -0,0 +1,160 @@
+import { ViewerStateControllerUseEffect } from './viewerState.useEffect'
+import { Observable, of } from 'rxjs'
+import { TestBed } from '@angular/core/testing'
+import { provideMockActions } from '@ngrx/effects/testing'
+import { provideMockStore } from '@ngrx/store/testing'
+import { defaultRootState, NEWVIEWER } from 'src/services/stateStore.service'
+import { Injectable } from '@angular/core'
+import { TemplateCoordinatesTransformation, ITemplateCoordXformResp } from 'src/services/templateCoordinatesTransformation.service'
+import { hot } from 'jasmine-marbles'
+import { VIEWERSTATE_CONTROLLER_ACTION_TYPES } from './viewerState.base'
+import { AngularMaterialModule } from '../sharedModules/angularMaterial.module'
+import { HttpClientModule } from '@angular/common/http'
+
+const bigbrainJson = require('!json-loader!src/res/ext/bigbrain.json')
+const colinJson = require('!json-loader!src/res/ext/colin.json')
+const colinJsonNehubaConfig = require('!json-loader!src/res/ext/colinNehubaConfig.json')
+const reconstitutedColin = {
+  ...colinJson,
+  nehubaConfig: colinJsonNehubaConfig
+}
+let returnPosition = null
+@Injectable()
+class MockCoordXformService{
+  getPointCoordinatesForTemplate(src:string, tgt: string, pos: [number, number, number]): Observable<ITemplateCoordXformResp>{
+    return returnPosition
+      ? of({ status: 'completed', result: returnPosition } as ITemplateCoordXformResp)
+      : of({ status: 'error', statusText: 'Failing query' } as ITemplateCoordXformResp)
+  }
+}
+
+const initialState = defaultRootState
+defaultRootState.viewerState.fetchedTemplates = [
+  bigbrainJson,
+  reconstitutedColin
+]
+initialState.viewerState.templateSelected = defaultRootState.viewerState.fetchedTemplates[0]
+const currentNavigation = {
+  position: [4, 5, 6],
+  orientation: [0, 0, 0, 1],
+  perspectiveOrientation: [ 0, 0, 0, 1],
+  perspectiveZoom: 2e5,
+  zoom: 1e5
+}
+initialState.viewerState.navigation = currentNavigation
+
+describe('viewerState.useEffect.ts', () => {
+  describe('ViewerStateControllerUseEffect', () => {
+    let actions$: Observable<any>
+    let spy: any
+    beforeEach(() => {
+
+      const mock = new MockCoordXformService()
+      spy = spyOn(mock, 'getPointCoordinatesForTemplate').and.callThrough()
+      returnPosition = null
+      actions$ = hot(
+        'a',
+        {
+          a: { 
+            type: VIEWERSTATE_CONTROLLER_ACTION_TYPES.SELECT_TEMPLATE_WITH_NAME,
+            payload: reconstitutedColin
+          }
+        }
+      )
+
+      TestBed.configureTestingModule({
+        imports: [
+          AngularMaterialModule,
+          HttpClientModule
+        ],
+        providers: [
+          ViewerStateControllerUseEffect,
+          provideMockActions(() => actions$),
+          provideMockStore({ initialState }),
+          {
+            provide: TemplateCoordinatesTransformation,
+            useValue: mock
+          }
+        ]
+      })
+    })
+
+    describe('selectTemplateWithName$', () => {
+
+      it('if coordXform returns error', () => {
+
+        const viewerStateCtrlEffect = TestBed.inject(ViewerStateControllerUseEffect)
+        expect(
+          viewerStateCtrlEffect.selectTemplateWithName$
+        ).toBeObservable(
+          hot(
+            'a',
+            {
+              a: {
+                type: NEWVIEWER,
+                selectTemplate: reconstitutedColin,
+                selectParcellation: reconstitutedColin.parcellations[0]
+              }
+            }
+          )
+        )
+      })
+
+      it('calls with correct param', () => {
+
+        // necessary for observable to fire
+        const viewerStateCtrlEffect = TestBed.inject(ViewerStateControllerUseEffect)
+        expect(
+          viewerStateCtrlEffect.selectTemplateWithName$
+        ).toBeObservable(
+          hot(
+            'a',
+            {
+              a: {
+                type: NEWVIEWER,
+                selectTemplate: reconstitutedColin,
+                selectParcellation: reconstitutedColin.parcellations[0]
+              }
+            }
+          )
+        )
+        expect(spy).toHaveBeenCalledWith(
+          bigbrainJson.name,
+          reconstitutedColin.name,
+          initialState.viewerState.navigation.position
+        )
+      })
+
+      it('if coordXform returns complete', () => {
+        returnPosition = [ 1.11e6, 2.22e6, 3.33e6 ]
+
+        const viewerStateCtrlEffect = TestBed.inject(ViewerStateControllerUseEffect)
+        const updatedColin = JSON.parse( JSON.stringify( reconstitutedColin ) )
+        const updatedColinNavigation = updatedColin.nehubaConfig.dataset.initialNgState.navigation
+
+        const { zoom, orientation, perspectiveOrientation, position, perspectiveZoom } = currentNavigation
+
+        for (const idx of [0, 1, 2]) {
+          updatedColinNavigation.pose.position.voxelCoordinates[idx] = returnPosition[idx] / updatedColinNavigation.pose.position.voxelSize[idx]
+        }
+        updatedColinNavigation.zoomFactor = zoom
+        updatedColinNavigation.pose.orientation = orientation
+
+        expect(
+          viewerStateCtrlEffect.selectTemplateWithName$
+        ).toBeObservable(
+          hot(
+            'a',
+            {
+              a: {
+                type: NEWVIEWER,
+                selectTemplate: updatedColin,
+                selectParcellation: updatedColin.parcellations[0]
+              }
+            }
+          )
+        )
+      })
+    })
+  })
+})
\ No newline at end of file
diff --git a/src/ui/viewerStateController/viewerState.useEffect.ts b/src/ui/viewerStateController/viewerState.useEffect.ts
index fddfc289321c62ce9c41bea08f83b91a5288f7e6..f849f6f24f62219970e5fe62c901e4cc8d287af9 100644
--- a/src/ui/viewerStateController/viewerState.useEffect.ts
+++ b/src/ui/viewerStateController/viewerState.useEffect.ts
@@ -1,8 +1,8 @@
 import { Injectable, OnDestroy, OnInit } from "@angular/core";
 import { Actions, Effect, ofType } from "@ngrx/effects";
 import { Action, select, Store } from "@ngrx/store";
-import { Observable, Subscription } from "rxjs";
-import {distinctUntilChanged, filter, map, mergeMap, shareReplay, withLatestFrom} from "rxjs/operators";
+import { Observable, Subscription, of } from "rxjs";
+import {distinctUntilChanged, filter, map, mergeMap, shareReplay, withLatestFrom, switchMap} from "rxjs/operators";
 import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service";
 import { CHANGE_NAVIGATION, FETCHED_TEMPLATE, GENERAL_ACTION_TYPES, IavRootStoreInterface, isDefined, NEWVIEWER, SELECT_PARCELLATION, SELECT_REGIONS } from "src/services/stateStore.service";
 import { UIService } from "src/services/uiService.service";
@@ -117,45 +117,36 @@ export class ViewerStateControllerUseEffect implements OnInit, OnDestroy {
         return name
       }),
       filter(name => !!name),
-      withLatestFrom(viewerState$.pipe(
-        select('templateSelected'),
-      )),
-      filter(([name,  templateSelected]) => {
-        if (templateSelected && templateSelected.name === name) { return false }
-        return true
-      }),
       withLatestFrom(
-        viewerState$.pipe(
-          select('fetchedTemplates'),
-        ),
-        viewerState$.pipe(
-          select('navigation'),
-        ),
+        viewerState$
       ),
-      mergeMap(([[name, templateSelected], availableTemplates, navigation]) =>
-        this.coordinatesTransformation.getPointCoordinatesForTemplate(templateSelected.name, name, navigation.position)
-          .then(res => {
-            navigation.position = res
-            return {
-              name: name,
-              templateSelected: templateSelected,
-              availableTemplates: availableTemplates,
-              coordinates: res,
-              navigation: navigation
+      switchMap(([newTemplateName, { templateSelected, fetchedTemplates, navigation }]) => {
+        const position = (navigation && navigation.position) || [0, 0, 0]
+        if (newTemplateName === templateSelected.name) return of(null)
+        return this.coordinatesTransformation.getPointCoordinatesForTemplate(templateSelected.name, newTemplateName, position).pipe(
+          map(({ status, statusText, result }) => {
+            if (status === 'error') {
+              return {
+                newTemplateName,
+                templateSelected: templateSelected,
+                fetchedTemplates,
+                translatedCoordinate: null,
+                navigation
+              }
             }
-          })
-          .catch(() => {
             return {
-              name: name,
+              newTemplateName,
               templateSelected: templateSelected,
-              availableTemplates: availableTemplates,
-              coordinates: null,
-              navigation: null
+              fetchedTemplates,
+              translatedCoordinate: result,
+              navigation
             }
           })
-      ),
-      map(({name, templateSelected, availableTemplates, coordinates, navigation}) => {
-        const newTemplateTobeSelected = availableTemplates.find(t => t.name === name)
+        )
+      }),
+      filter(v => !!v),
+      map(({ newTemplateName, templateSelected, fetchedTemplates, translatedCoordinate, navigation }) => {
+        const newTemplateTobeSelected = fetchedTemplates.find(t => t.name === newTemplateName)
         if (!newTemplateTobeSelected) {
           return {
             type: GENERAL_ACTION_TYPES.ERROR,
@@ -165,19 +156,23 @@ export class ViewerStateControllerUseEffect implements OnInit, OnDestroy {
           }
         }
 
-        if (!coordinates && !navigation)
+        if (!translatedCoordinate) {
           return {
             type: NEWVIEWER,
             selectTemplate: newTemplateTobeSelected,
             selectParcellation: newTemplateTobeSelected.parcellations[0],
           }
-
+        }
         const deepCopiedState = JSON.parse(JSON.stringify(newTemplateTobeSelected))
         const initNavigation = deepCopiedState.nehubaConfig.dataset.initialNgState.navigation
 
-        initNavigation.zoomFactor = navigation.zoom
-        initNavigation.pose.position.voxelCoordinates = coordinates.map((c, i) => c/initNavigation.pose.position.voxelSize[i])
-        initNavigation.pose.orientation = navigation.orientation
+        const { zoom = null, orientation = null } = navigation || {}
+        if (zoom) initNavigation.zoomFactor = zoom
+        if (orientation) initNavigation.pose.orientation = orientation
+
+        for (const idx of [0, 1, 2]) {
+          initNavigation.pose.position.voxelCoordinates[idx] = translatedCoordinate[idx] / initNavigation.pose.position.voxelSize[idx]
+        }
 
         return {
           type: NEWVIEWER,