diff --git a/.env b/.env
index 470be3d78ae1c38d2951c67acae288685d447b9c..b3e48b427a49bb631ab1a95914bf2c1beec5fe37 100644
--- a/.env
+++ b/.env
@@ -1,6 +1,6 @@
 
-NEXT_PUBLIC_BASE_API_URL = 'https://corsproxy.io/?https://lux1.atcomp.pl/minerva/api'
-NEXT_PUBLIC_BASE_NEW_API_URL = 'https://corsproxy.io/?https://lux1.atcomp.pl/minerva/new_api/'
+NEXT_PUBLIC_BASE_API_URL = 'https://lux1.atcomp.pl/minerva/api'
+NEXT_PUBLIC_BASE_NEW_API_URL = 'https://lux1.atcomp.pl/minerva/new_api/'
 BASE_MAP_IMAGES_URL = 'https://lux1.atcomp.pl/'
 NEXT_PUBLIC_PROJECT_ID = 'pdmap_appu_test'
 ZOD_SEED = 997
diff --git a/package-lock.json b/package-lock.json
index f41b112587042b8d19ebbab8add5288dcd762146..79ab1fa87d504d5a0522547ceebce4f5a23fe7ee 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -29,6 +29,8 @@
         "react-redux": "^8.1.2",
         "tailwind-merge": "^1.14.0",
         "tailwindcss": "3.3.3",
+        "ts-deepmerge": "^6.2.0",
+        "use-debounce": "^9.0.4",
         "zod": "^3.22.2"
       },
       "devDependencies": {
@@ -12316,6 +12318,14 @@
         "typescript": ">=4.2.0"
       }
     },
+    "node_modules/ts-deepmerge": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmjs.org/ts-deepmerge/-/ts-deepmerge-6.2.0.tgz",
+      "integrity": "sha512-2qxI/FZVDPbzh63GwWIZYE7daWKtwXZYuyc8YNq0iTmMUwn4mL0jRLsp6hfFlgbdRSR4x2ppe+E86FnvEpN7Nw==",
+      "engines": {
+        "node": ">=14.13.1"
+      }
+    },
     "node_modules/ts-interface-checker": {
       "version": "0.1.13",
       "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
@@ -12627,6 +12637,17 @@
         "requires-port": "^1.0.0"
       }
     },
+    "node_modules/use-debounce": {
+      "version": "9.0.4",
+      "resolved": "https://registry.npmjs.org/use-debounce/-/use-debounce-9.0.4.tgz",
+      "integrity": "sha512-6X8H/mikbrt0XE8e+JXRtZ8yYVvKkdYRfmIhWZYsP8rcNs9hk3APV8Ua2mFkKRLcJKVdnX2/Vwrmg2GWKUQEaQ==",
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "peerDependencies": {
+        "react": ">=16.8.0"
+      }
+    },
     "node_modules/use-sync-external-store": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
diff --git a/package.json b/package.json
index c6c19cd69978bfb0f35c523f619b33883299f8d2..cc259882ab521253bac4b434e85f01e7d3f26885 100644
--- a/package.json
+++ b/package.json
@@ -43,6 +43,8 @@
     "react-redux": "^8.1.2",
     "tailwind-merge": "^1.14.0",
     "tailwindcss": "3.3.3",
+    "ts-deepmerge": "^6.2.0",
+    "use-debounce": "^9.0.4",
     "zod": "^3.22.2"
   },
   "devDependencies": {
diff --git a/src/components/Map/MapViewer/MapViewer.types.ts b/src/components/Map/MapViewer/MapViewer.types.ts
index babe85b11c2b8f13358119061cf17ae39cf2a54a..2cc15d5da01ea0a5ab0cec43e9f0abacc762f790 100644
--- a/src/components/Map/MapViewer/MapViewer.types.ts
+++ b/src/components/Map/MapViewer/MapViewer.types.ts
@@ -1,3 +1,10 @@
 import Map from 'ol/Map';
+import View from 'ol/View';
+import BaseLayer from 'ol/layer/Base';
 
 export type MapInstance = Map | undefined;
+
+export type MapConfig = {
+  view: View;
+  layers: BaseLayer[];
+};
diff --git a/src/components/Map/MapViewer/utils/config/useOlMapConfig.test.ts b/src/components/Map/MapViewer/utils/config/useOlMapConfig.test.ts
deleted file mode 100644
index e2873af0aa32bfea673a0184ab46ae7bca804cd5..0000000000000000000000000000000000000000
--- a/src/components/Map/MapViewer/utils/config/useOlMapConfig.test.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-describe('useOlMapConfig - util', () => {
-  // TODO: tests
-  // TileLayer is mocked in the file, so we need to firstly wait for module API connection
-
-  it('noop', () => {
-    // eslint-disable-next-line no-magic-numbers
-    expect(1).toEqual(1);
-  });
-});
diff --git a/src/components/Map/MapViewer/utils/config/useOlMapConfig.ts b/src/components/Map/MapViewer/utils/config/useOlMapConfig.ts
deleted file mode 100644
index 2a584d8764593a4cfaa69b8d98e33cfe3b172159..0000000000000000000000000000000000000000
--- a/src/components/Map/MapViewer/utils/config/useOlMapConfig.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-/* eslint-disable no-magic-numbers */
-import { OPTIONS } from '@/constants/map';
-import { currentBackgroundImagePathSelector } from '@/redux/backgrounds/background.selectors';
-import { mapDataPositionSelector, mapDataSizeSelector } from '@/redux/map/map.selectors';
-import { projectDataSelector } from '@/redux/project/project.selectors';
-import { Point } from '@/types/map';
-import { usePointToProjection } from '@/utils/map/usePointToProjection';
-import { View } from 'ol';
-import BaseLayer from 'ol/layer/Base';
-import TileLayer from 'ol/layer/Tile';
-import { XYZ } from 'ol/source';
-import { useMemo } from 'react';
-import { useSelector } from 'react-redux';
-import { getMapTileUrl } from './getMapTileUrl';
-
-interface UseOlMapConfigResult {
-  view: View;
-  layers: BaseLayer[];
-}
-
-export const useOlMapConfig = (): UseOlMapConfigResult => {
-  const mapPosition = useSelector(mapDataPositionSelector);
-  const mapSize = useSelector(mapDataSizeSelector);
-  const currentBackgroundImagePath = useSelector(currentBackgroundImagePathSelector);
-  const project = useSelector(projectDataSelector);
-  const pointToProjection = usePointToProjection();
-
-  const center = useMemo(() => {
-    const centerPoint: Point = {
-      x: mapPosition.x,
-      y: mapPosition.y,
-    };
-
-    return pointToProjection(centerPoint);
-  }, [mapPosition, pointToProjection]);
-
-  const view = useMemo(
-    () =>
-      new View({
-        center,
-        zoom: mapPosition.z,
-        showFullExtent: OPTIONS.showFullExtent,
-      }),
-    [center, mapPosition],
-  );
-
-  const tileLayer = useMemo(
-    (): TileLayer<XYZ> =>
-      new TileLayer({
-        visible: true,
-        source: new XYZ({
-          url: getMapTileUrl({ projectDirectory: project?.directory, currentBackgroundImagePath }),
-          maxZoom: mapSize.maxZoom,
-          minZoom: mapSize.minZoom,
-          tileSize: mapSize.tileSize,
-          wrapX: OPTIONS.wrapXInTileLayer,
-        }),
-      }),
-    [mapSize, currentBackgroundImagePath, project?.directory],
-  );
-
-  return {
-    view,
-    layers: [tileLayer],
-  };
-};
diff --git a/src/components/Map/MapViewer/utils/config/useOlMapLayers.test.ts b/src/components/Map/MapViewer/utils/config/useOlMapLayers.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f2c512a5cd10a2b0bf78f73bd6b9e62c44906f6b
--- /dev/null
+++ b/src/components/Map/MapViewer/utils/config/useOlMapLayers.test.ts
@@ -0,0 +1,94 @@
+/* eslint-disable no-magic-numbers */
+import { MAP_DATA_INITIAL_STATE } from '@/redux/map/map.constants';
+import mapSlice from '@/redux/map/map.slice';
+import { getReduxWrapperUsingSliceReducer } from '@/utils/testing/getReduxWrapperUsingSliceReducer';
+import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore';
+import { renderHook, waitFor } from '@testing-library/react';
+import { Map } from 'ol';
+import TileLayer from 'ol/layer/Tile';
+import React from 'react';
+import { useOlMap } from '../useOlMap';
+import { useOlMapLayers } from './useOlMapLayers';
+
+const useRefValue = {
+  current: null,
+};
+
+Object.defineProperty(useRefValue, 'current', {
+  get: jest.fn(() => ({
+    innerHTML: '',
+    appendChild: jest.fn(),
+    addEventListener: jest.fn(),
+    getRootNode: jest.fn(),
+  })),
+  set: jest.fn(() => ({
+    innerHTML: '',
+    appendChild: jest.fn(),
+    addEventListener: jest.fn(),
+    getRootNode: jest.fn(),
+  })),
+});
+
+jest.spyOn(React, 'useRef').mockReturnValue(useRefValue);
+
+describe('useOlMapLayers - util', () => {
+  it('should modify layers of the map instance on init', async () => {
+    const { Wrapper } = getReduxWrapperUsingSliceReducer('map', mapSlice);
+    const dummyElement = document.createElement('div');
+    const mapInstance = new Map({ target: dummyElement });
+    const setLayersSpy = jest.spyOn(mapInstance, 'setLayers');
+    const CALLED_ONCE = 1;
+
+    renderHook(() => useOlMapLayers({ mapInstance }), {
+      wrapper: Wrapper,
+    });
+
+    await waitFor(() => expect(setLayersSpy).toBeCalledTimes(CALLED_ONCE));
+  });
+
+  it('should return valid View instance', async () => {
+    const { Wrapper } = getReduxWrapperWithStore({
+      map: {
+        data: {
+          ...MAP_DATA_INITIAL_STATE,
+          size: {
+            width: 256,
+            height: 256,
+            tileSize: 256,
+            minZoom: 1,
+            maxZoom: 1,
+          },
+          position: {
+            initial: {
+              x: 256,
+              y: 256,
+            },
+            last: {
+              x: 256,
+              y: 256,
+            },
+          },
+        },
+        loading: 'idle',
+        error: {
+          name: '',
+          message: '',
+        },
+      },
+    });
+    const dummyElement = document.createElement('div');
+    const { result: hohResult } = renderHook(() => useOlMap({ target: dummyElement }), {
+      wrapper: Wrapper,
+    });
+
+    const { result } = renderHook(
+      () => useOlMapLayers({ mapInstance: hohResult.current.mapInstance }),
+      {
+        wrapper: Wrapper,
+      },
+    );
+
+    expect(result.current[0]).toBeInstanceOf(TileLayer);
+    expect(result.current[0].getSourceState()).toBe('ready');
+  });
+});
diff --git a/src/components/Map/MapViewer/utils/config/useOlMapLayers.ts b/src/components/Map/MapViewer/utils/config/useOlMapLayers.ts
new file mode 100644
index 0000000000000000000000000000000000000000..67d71d50060f40018ec5d515b0608a8b98bb4f92
--- /dev/null
+++ b/src/components/Map/MapViewer/utils/config/useOlMapLayers.ts
@@ -0,0 +1,57 @@
+/* eslint-disable no-magic-numbers */
+import { OPTIONS } from '@/constants/map';
+import { currentBackgroundImagePathSelector } from '@/redux/backgrounds/background.selectors';
+import { mapDataSizeSelector } from '@/redux/map/map.selectors';
+import { projectDataSelector } from '@/redux/project/project.selectors';
+import TileLayer from 'ol/layer/Tile';
+import { XYZ } from 'ol/source';
+import { useEffect, useMemo } from 'react';
+import { useSelector } from 'react-redux';
+import { MapConfig, MapInstance } from '../../MapViewer.types';
+import { getMapTileUrl } from './getMapTileUrl';
+
+interface UseOlMapLayersInput {
+  mapInstance: MapInstance;
+}
+
+export const useOlMapLayers = ({ mapInstance }: UseOlMapLayersInput): MapConfig['layers'] => {
+  const mapSize = useSelector(mapDataSizeSelector);
+  const currentBackgroundImagePath = useSelector(currentBackgroundImagePathSelector);
+  const project = useSelector(projectDataSelector);
+
+  const sourceUrl = useMemo(
+    () => getMapTileUrl({ projectDirectory: project?.directory, currentBackgroundImagePath }),
+    [project?.directory, currentBackgroundImagePath],
+  );
+
+  const source = useMemo(
+    () =>
+      new XYZ({
+        url: sourceUrl,
+        maxZoom: mapSize.maxZoom,
+        minZoom: mapSize.minZoom,
+        tileSize: mapSize.tileSize,
+        wrapX: OPTIONS.wrapXInTileLayer,
+      }),
+    [sourceUrl, mapSize.maxZoom, mapSize.minZoom, mapSize.tileSize],
+  );
+
+  const tileLayer = useMemo(
+    (): TileLayer<XYZ> =>
+      new TileLayer({
+        visible: true,
+        source,
+      }),
+    [source],
+  );
+
+  useEffect(() => {
+    if (!mapInstance) {
+      return;
+    }
+
+    mapInstance.setLayers([tileLayer]);
+  }, [tileLayer, mapInstance]);
+
+  return [tileLayer];
+};
diff --git a/src/components/Map/MapViewer/utils/config/useOlMapView.test.ts b/src/components/Map/MapViewer/utils/config/useOlMapView.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8a78f734250e8f020e17ae8093f0af4f33d3ee0f
--- /dev/null
+++ b/src/components/Map/MapViewer/utils/config/useOlMapView.test.ts
@@ -0,0 +1,107 @@
+/* eslint-disable no-magic-numbers */
+import mapSlice, { setMapData } from '@/redux/map/map.slice';
+import { getReduxWrapperUsingSliceReducer } from '@/utils/testing/getReduxWrapperUsingSliceReducer';
+import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore';
+import { renderHook, waitFor } from '@testing-library/react';
+import { View } from 'ol';
+import Map from 'ol/Map';
+import React from 'react';
+import { MAP_DATA_INITIAL_STATE } from '../../../../../redux/map/map.constants';
+import { useOlMap } from '../useOlMap';
+import { useOlMapView } from './useOlMapView';
+
+const useRefValue = {
+  current: null,
+};
+
+Object.defineProperty(useRefValue, 'current', {
+  get: jest.fn(() => ({
+    innerHTML: '',
+    appendChild: jest.fn(),
+    addEventListener: jest.fn(),
+    getRootNode: jest.fn(),
+  })),
+  set: jest.fn(() => ({
+    innerHTML: '',
+    appendChild: jest.fn(),
+    addEventListener: jest.fn(),
+    getRootNode: jest.fn(),
+  })),
+});
+
+jest.spyOn(React, 'useRef').mockReturnValue(useRefValue);
+
+describe('useOlMapView - util', () => {
+  it('should modify view of the map instance on INITIAL position config change', async () => {
+    const { Wrapper, store } = getReduxWrapperUsingSliceReducer('map', mapSlice);
+    const dummyElement = document.createElement('div');
+    const { result: hohResult } = renderHook(() => useOlMap({ target: dummyElement }), {
+      wrapper: Wrapper,
+    });
+    const setViewSpy = jest.spyOn(hohResult.current.mapInstance as Map, 'setView');
+    const CALLED_ONCE = 1;
+
+    store.dispatch(
+      setMapData({
+        position: {
+          initial: {
+            x: 0,
+            y: 0,
+          },
+        },
+      }),
+    );
+
+    renderHook(() => useOlMapView({ mapInstance: hohResult.current.mapInstance }), {
+      wrapper: Wrapper,
+    });
+
+    await waitFor(() => expect(setViewSpy).toBeCalledTimes(CALLED_ONCE));
+  });
+
+  it('should return valid View instance', async () => {
+    const { Wrapper } = getReduxWrapperWithStore({
+      map: {
+        data: {
+          ...MAP_DATA_INITIAL_STATE,
+          size: {
+            width: 256,
+            height: 256,
+            tileSize: 256,
+            minZoom: 1,
+            maxZoom: 1,
+          },
+          position: {
+            initial: {
+              x: 256,
+              y: 256,
+            },
+            last: {
+              x: 256,
+              y: 256,
+            },
+          },
+        },
+        loading: 'idle',
+        error: {
+          name: '',
+          message: '',
+        },
+      },
+    });
+    const dummyElement = document.createElement('div');
+    const { result: hohResult } = renderHook(() => useOlMap({ target: dummyElement }), {
+      wrapper: Wrapper,
+    });
+
+    const { result } = renderHook(
+      () => useOlMapView({ mapInstance: hohResult.current.mapInstance }),
+      {
+        wrapper: Wrapper,
+      },
+    );
+
+    expect(result.current).toBeInstanceOf(View);
+    expect(result.current.getCenter()).toStrictEqual([0, -0]);
+  });
+});
diff --git a/src/components/Map/MapViewer/utils/config/useOlMapView.ts b/src/components/Map/MapViewer/utils/config/useOlMapView.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9dc00a261aaed3dfc9c4f3dda0187755d0ae9c7e
--- /dev/null
+++ b/src/components/Map/MapViewer/utils/config/useOlMapView.ts
@@ -0,0 +1,53 @@
+/* eslint-disable no-magic-numbers */
+import { OPTIONS } from '@/constants/map';
+import { mapDataInitialPositionSelector } from '@/redux/map/map.selectors';
+import { Point } from '@/types/map';
+import { usePointToProjection } from '@/utils/map/usePointToProjection';
+import { View } from 'ol';
+import { useEffect, useMemo } from 'react';
+import { useSelector } from 'react-redux';
+import { MapConfig, MapInstance } from '../../MapViewer.types';
+
+interface UseOlMapViewInput {
+  mapInstance: MapInstance;
+}
+
+export const useOlMapView = ({ mapInstance }: UseOlMapViewInput): MapConfig['view'] => {
+  const mapInitialPosition = useSelector(mapDataInitialPositionSelector);
+  const pointToProjection = usePointToProjection();
+
+  const center = useMemo((): Point => {
+    const centerPoint: Point = {
+      x: mapInitialPosition.x,
+      y: mapInitialPosition.y,
+    };
+
+    const [x, y] = pointToProjection(centerPoint);
+
+    return {
+      x,
+      y,
+    };
+  }, [mapInitialPosition, pointToProjection]);
+
+  const viewConfig = useMemo(
+    () => ({
+      center: [center.x, center.y],
+      zoom: mapInitialPosition.z,
+      showFullExtent: OPTIONS.showFullExtent,
+    }),
+    [center.x, center.y, mapInitialPosition.z],
+  );
+
+  const view = useMemo(() => new View(viewConfig), [viewConfig]);
+
+  useEffect(() => {
+    if (!mapInstance) {
+      return;
+    }
+
+    mapInstance.setView(view);
+  }, [view, mapInstance]);
+
+  return view;
+};
diff --git a/src/components/Map/MapViewer/utils/listeners/onMapPositionChange.ts b/src/components/Map/MapViewer/utils/listeners/onMapPositionChange.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a0164f94774dd3b8921e1e6ef0aac88aaec0fd23
--- /dev/null
+++ b/src/components/Map/MapViewer/utils/listeners/onMapPositionChange.ts
@@ -0,0 +1,28 @@
+import { setMapData } from '@/redux/map/map.slice';
+import { MapSize } from '@/redux/map/map.types';
+import { AppDispatch } from '@/redux/store';
+import { Point } from '@/types/map';
+import { latLngToPoint } from '@/utils/map/latLngToPoint';
+import { toLonLat } from 'ol/proj';
+import { ObjectEvent } from 'openlayers';
+
+/* prettier-ignore */
+export const onMapPositionChange =
+  (mapSize: MapSize, mapPosition: Point, dispatch: AppDispatch) =>
+    (e: ObjectEvent): void => {
+      // eslint-disable-next-line no-underscore-dangle
+      const { center, zoom } = e.target.values_;
+      const [lng, lat] = toLonLat(center);
+      const value = latLngToPoint([lat, lng], mapSize, { rounded: true });
+
+      dispatch(
+        setMapData({
+          position: {
+            last: {
+              ...value,
+              z: Math.round(zoom),
+            }
+          }
+        }),
+      );
+    };
diff --git a/src/components/Map/MapViewer/utils/listeners/useOlMapListeners.test.ts b/src/components/Map/MapViewer/utils/listeners/useOlMapListeners.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..20d0401abf66cd12e2ab5578c637963cf56b7c54
--- /dev/null
+++ b/src/components/Map/MapViewer/utils/listeners/useOlMapListeners.test.ts
@@ -0,0 +1,39 @@
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+import mapSlice from '@/redux/map/map.slice';
+import { getReduxWrapperUsingSliceReducer } from '@/utils/testing/getReduxWrapperUsingSliceReducer';
+import { renderHook } from '@testing-library/react';
+import { View } from 'ol';
+import * as positionListener from './onMapPositionChange';
+import { useOlMapListeners } from './useOlMapListeners';
+
+jest.mock('./onMapPositionChange', () => ({
+  __esModule: true,
+  onMapPositionChange: jest.fn(),
+}));
+
+jest.mock('use-debounce', () => {
+  return {
+    useDebounce: () => {},
+    useDebouncedCallback: () => {},
+  };
+});
+
+describe('useOlMapListeners - util', () => {
+  const { Wrapper } = getReduxWrapperUsingSliceReducer('map', mapSlice);
+
+  beforeEach(() => {
+    jest.clearAllMocks();
+  });
+
+  describe('on change:center view event', () => {
+    it('should run onMapPositionChange event', () => {
+      const view = new View();
+      const CALLED_ONCE = 1;
+
+      renderHook(() => useOlMapListeners({ view }), { wrapper: Wrapper });
+      view.dispatchEvent('change:center');
+
+      expect(positionListener.onMapPositionChange).toBeCalledTimes(CALLED_ONCE);
+    });
+  });
+});
diff --git a/src/components/Map/MapViewer/utils/listeners/useOlMapListeners.ts b/src/components/Map/MapViewer/utils/listeners/useOlMapListeners.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ec88619565b10bb91760b36c50ef199fbe8cd61e
--- /dev/null
+++ b/src/components/Map/MapViewer/utils/listeners/useOlMapListeners.ts
@@ -0,0 +1,28 @@
+import { OPTIONS } from '@/constants/map';
+import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
+import { mapDataLastPositionSelector, mapDataSizeSelector } from '@/redux/map/map.selectors';
+import { View } from 'ol';
+import { useEffect } from 'react';
+import { useSelector } from 'react-redux';
+import { useDebouncedCallback } from 'use-debounce';
+import { onMapPositionChange } from './onMapPositionChange';
+
+interface UseOlMapListenersInput {
+  view: View;
+}
+
+export const useOlMapListeners = ({ view }: UseOlMapListenersInput): void => {
+  const mapSize = useSelector(mapDataSizeSelector);
+  const mapLastPosition = useSelector(mapDataLastPositionSelector);
+  const dispatch = useAppDispatch();
+
+  const handleChangeCenter = useDebouncedCallback(
+    onMapPositionChange(mapSize, mapLastPosition, dispatch),
+    OPTIONS.queryPersistTime,
+    { leading: false },
+  );
+
+  useEffect(() => {
+    view.on('change:center', handleChangeCenter);
+  }, [view, handleChangeCenter]);
+};
diff --git a/src/components/Map/MapViewer/utils/useOlMap.test.ts b/src/components/Map/MapViewer/utils/useOlMap.test.ts
index c606b77abe4d46c7db675e0a6a68eda9bf0d78b2..29cf1fc029a9296492288ab0304e851d49e8119c 100644
--- a/src/components/Map/MapViewer/utils/useOlMap.test.ts
+++ b/src/components/Map/MapViewer/utils/useOlMap.test.ts
@@ -1,4 +1,4 @@
-import mapSlice, { setMapData } from '@/redux/map/map.slice';
+import mapSlice from '@/redux/map/map.slice';
 import { getReduxWrapperUsingSliceReducer } from '@/utils/testing/getReduxWrapperUsingSliceReducer';
 import { renderHook, waitFor } from '@testing-library/react';
 import { Map } from 'ol';
@@ -27,7 +27,7 @@ Object.defineProperty(useRefValue, 'current', {
 jest.spyOn(React, 'useRef').mockReturnValue(useRefValue);
 
 describe('useOlMap - util', () => {
-  const { Wrapper, store } = getReduxWrapperUsingSliceReducer('map', mapSlice);
+  const { Wrapper } = getReduxWrapperUsingSliceReducer('map', mapSlice);
 
   describe('when initializing', () => {
     it('should set map instance', async () => {
@@ -44,45 +44,4 @@ describe('useOlMap - util', () => {
       expect(dummyElement.childNodes[FIRST_NODE]).toHaveClass('ol-viewport');
     });
   });
-
-  describe('when initialized', () => {
-    it('should modify view of the map instance on position config change', async () => {
-      const dummyElement = document.createElement('div');
-      const { result } = renderHook(() => useOlMap({ target: dummyElement }), { wrapper: Wrapper });
-      const setViewSpy = jest.spyOn(result.current.mapInstance as Map, 'setView');
-      const CALLED_ONCE = 1;
-
-      store.dispatch(
-        setMapData({
-          position: {
-            x: 0,
-            y: 0,
-          },
-        }),
-      );
-
-      await waitFor(() => expect(setViewSpy).toBeCalledTimes(CALLED_ONCE));
-    });
-
-    it('should modify layers of the map instance on size config change', async () => {
-      const dummyElement = document.createElement('div');
-      const { result } = renderHook(() => useOlMap({ target: dummyElement }), { wrapper: Wrapper });
-      const setLayersSpy = jest.spyOn(result.current.mapInstance as Map, 'setLayers');
-      const CALLED_ONCE = 1;
-
-      store.dispatch(
-        setMapData({
-          size: {
-            maxZoom: 10,
-            minZoom: 2,
-            tileSize: 256,
-            width: 1000,
-            height: 1000,
-          },
-        }),
-      );
-
-      await waitFor(() => expect(setLayersSpy).toBeCalledTimes(CALLED_ONCE));
-    });
-  });
 });
diff --git a/src/components/Map/MapViewer/utils/useOlMap.ts b/src/components/Map/MapViewer/utils/useOlMap.ts
index ca82591af5077a935f1a5833451885696e70a81c..a80407e303374c2da8c3829dfe0b8d66824918c4 100644
--- a/src/components/Map/MapViewer/utils/useOlMap.ts
+++ b/src/components/Map/MapViewer/utils/useOlMap.ts
@@ -1,7 +1,9 @@
 import Map from 'ol/Map';
 import React, { MutableRefObject, useEffect, useState } from 'react';
 import { MapInstance } from '../MapViewer.types';
-import { useOlMapConfig } from './config/useOlMapConfig';
+import { useOlMapLayers } from './config/useOlMapLayers';
+import { useOlMapView } from './config/useOlMapView';
+import { useOlMapListeners } from './listeners/useOlMapListeners';
 
 interface UseOlMapInput {
   target?: HTMLElement;
@@ -16,7 +18,9 @@ type UseOlMap = (input?: UseOlMapInput) => UseOlMapOutput;
 export const useOlMap: UseOlMap = ({ target } = {}) => {
   const mapRef = React.useRef<null | HTMLDivElement>(null);
   const [mapInstance, setMapInstance] = useState<MapInstance>(undefined);
-  const mapConfig = useOlMapConfig();
+  const view = useOlMapView({ mapInstance });
+  useOlMapLayers({ mapInstance });
+  useOlMapListeners({ view });
 
   useEffect(() => {
     // checking if innerHTML is empty due to possibility of target element cloning by openlayers map instance
@@ -31,15 +35,6 @@ export const useOlMap: UseOlMap = ({ target } = {}) => {
     setMapInstance(currentMap => currentMap || map);
   }, [target]);
 
-  useEffect(() => {
-    if (!mapInstance) {
-      return;
-    }
-
-    mapInstance.setView(mapConfig.view);
-    mapInstance.setLayers(mapConfig.layers);
-  }, [mapConfig, mapInstance]);
-
   return {
     mapRef,
     mapInstance,
diff --git a/src/components/SPA/MinervaSPA.component.tsx b/src/components/SPA/MinervaSPA.component.tsx
index 856e3487b746b504123995ebdf46329736f81787..4de3ca1fb95c0e3bbf4b82dbc85e7afffb32d033 100644
--- a/src/components/SPA/MinervaSPA.component.tsx
+++ b/src/components/SPA/MinervaSPA.component.tsx
@@ -1,8 +1,9 @@
 import { FunctionalArea } from '@/components/FunctionalArea';
 import { Map } from '@/components/Map';
+import { useReduxBusQueryManager } from '@/utils/query-manager/useReduxBusQueryManager';
 import { Manrope } from '@next/font/google';
 import { twMerge } from 'tailwind-merge';
-import { useInitializeStore } from './utils/useInitializeStore';
+import { useInitializeStore } from '../../utils/initialize/useInitializeStore';
 
 const manrope = Manrope({
   variable: '--font-manrope',
@@ -13,6 +14,7 @@ const manrope = Manrope({
 
 export const MinervaSPA = (): JSX.Element => {
   useInitializeStore();
+  useReduxBusQueryManager();
 
   return (
     <div className={twMerge('relative', manrope.variable)}>
diff --git a/src/constants/map.ts b/src/constants/map.ts
index 83fbec09ffae3560d187f82cd564aed44a976c02..765b30b0ea67439b1de5933022a122fd2a7811da 100644
--- a/src/constants/map.ts
+++ b/src/constants/map.ts
@@ -1,5 +1,6 @@
 import { LatLng, Point } from '@/types/map';
 import { z } from 'zod';
+import { HALF_SECOND_MS } from './time';
 
 export const DEFAULT_TILE_SIZE = 256;
 export const DEFAULT_MIN_ZOOM = 2;
@@ -19,6 +20,7 @@ export const DEFAULT_CENTER_POINT: Point = {
 export const OPTIONS = {
   showFullExtent: false,
   wrapXInTileLayer: false,
+  queryPersistTime: HALF_SECOND_MS,
 };
 
 export const VALID_MAP_SIZE_SCHEMA = z.object({
diff --git a/src/constants/time.ts b/src/constants/time.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0cb37b138acc5057e5b395567743831169c10fca
--- /dev/null
+++ b/src/constants/time.ts
@@ -0,0 +1 @@
+export const HALF_SECOND_MS = 500;
diff --git a/src/redux/map/map.constants.ts b/src/redux/map/map.constants.ts
index ba88a03811be7e0e0d08fe58bb13ceaff6f51d65..34be216db6c6b3a7eabe6ca7fe6a11fb6d06ad08 100644
--- a/src/redux/map/map.constants.ts
+++ b/src/redux/map/map.constants.ts
@@ -13,7 +13,10 @@ export const MAP_DATA_INITIAL_STATE: MapData = {
   modelId: 0,
   backgroundId: 0,
   overlaysIds: [],
-  position: DEFAULT_CENTER_POINT,
+  position: {
+    last: DEFAULT_CENTER_POINT,
+    initial: DEFAULT_CENTER_POINT,
+  },
   show: {
     legend: false,
     comments: false,
diff --git a/src/redux/map/map.reducers.ts b/src/redux/map/map.reducers.ts
index 962704b36070701d2e763147d57f2f99c8751ae8..25099909bee3b89f30a9f4106ac03004c0c6e330 100644
--- a/src/redux/map/map.reducers.ts
+++ b/src/redux/map/map.reducers.ts
@@ -1,9 +1,22 @@
 import { ActionReducerMapBuilder } from '@reduxjs/toolkit';
+import merge from 'ts-deepmerge';
+import { getPointMerged } from '../../utils/object/getPointMerged';
 import { initMapData } from './map.thunks';
 import { MapState, SetMapDataAction } from './map.types';
 
 export const setMapDataReducer = (state: MapState, action: SetMapDataAction): void => {
-  state.data = { ...state.data, ...action.payload };
+  const payload = action.payload || {};
+  const payloadPosition = payload?.position || {};
+  const statePosition = state.data.position;
+
+  state.data = {
+    ...state.data,
+    ...payload,
+    position: {
+      initial: getPointMerged(payloadPosition?.initial || {}, statePosition.initial),
+      last: getPointMerged(payloadPosition?.last || {}, statePosition.last),
+    },
+  };
 };
 
 export const getMapReducers = (builder: ActionReducerMapBuilder<MapState>): void => {
@@ -12,7 +25,7 @@ export const getMapReducers = (builder: ActionReducerMapBuilder<MapState>): void
   });
   builder.addCase(initMapData.fulfilled, (state, action) => {
     const payload = action.payload || {};
-    state.data = { ...state.data, ...payload };
+    state.data = merge(state.data, payload);
     state.loading = 'succeeded';
   });
   builder.addCase(initMapData.rejected, state => {
diff --git a/src/redux/map/map.selectors.ts b/src/redux/map/map.selectors.ts
index e5bbcfe7dc366b0154edc21775477bb38be82973..bc71ec9840e6bd2c6700461b528082e707bfd217 100644
--- a/src/redux/map/map.selectors.ts
+++ b/src/redux/map/map.selectors.ts
@@ -1,8 +1,20 @@
 import { rootSelector } from '@/redux/root/root.selectors';
 import { createSelector } from '@reduxjs/toolkit';
 
-export const mapDataSelector = createSelector(rootSelector, state => state.map.data);
+export const mapSelector = createSelector(rootSelector, state => state.map);
+
+export const mapDataSelector = createSelector(mapSelector, map => map.data);
 
 export const mapDataSizeSelector = createSelector(mapDataSelector, map => map.size);
 
 export const mapDataPositionSelector = createSelector(mapDataSelector, map => map.position);
+
+export const mapDataInitialPositionSelector = createSelector(
+  mapDataPositionSelector,
+  position => position.initial,
+);
+
+export const mapDataLastPositionSelector = createSelector(
+  mapDataPositionSelector,
+  position => position.last,
+);
diff --git a/src/redux/map/map.thunks.test.ts b/src/redux/map/map.thunks.test.ts
index b14e744dab577afaa1b8f25b56d76f2162053908..d717ad71b5c557ab908eed821bdd5f1616325c01 100644
--- a/src/redux/map/map.thunks.test.ts
+++ b/src/redux/map/map.thunks.test.ts
@@ -2,6 +2,7 @@ import { PROJECT_ID } from '@/constants';
 import { backgroundsFixture } from '@/models/fixtures/backgroundsFixture';
 import { modelsFixture } from '@/models/fixtures/modelsFixture';
 import { overlaysFixture } from '@/models/fixtures/overlaysFixture';
+import { QueryData } from '@/types/query';
 import { mockNetworkResponse } from '@/utils/mockNetworkResponse';
 import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore';
 import { HttpStatusCode } from 'axios';
@@ -15,6 +16,12 @@ import { InitMapDataActionPayload } from './map.types';
 
 const mockedAxiosClient = mockNetworkResponse();
 
+const EMPTY_QUERY_DATA: QueryData = {
+  modelId: undefined,
+  backgroundId: undefined,
+  initialPosition: undefined,
+};
+
 describe('map thunks', () => {
   describe('initMapData - thunk', () => {
     describe('when API is returning valid data', () => {
@@ -33,7 +40,8 @@ describe('map thunks', () => {
 
         store = getReduxWrapperWithStore().store;
         const dispatch = store.dispatch as AppDispatch;
-        payload = (await dispatch(initMapData())).payload as InitMapDataActionPayload;
+        payload = (await dispatch(initMapData({ queryData: EMPTY_QUERY_DATA })))
+          .payload as InitMapDataActionPayload;
       });
 
       it('should fetch backgrounds data in store', async () => {
@@ -76,7 +84,8 @@ describe('map thunks', () => {
 
         store = getReduxWrapperWithStore().store;
         const dispatch = store.dispatch as AppDispatch;
-        payload = (await dispatch(initMapData())).payload as InitMapDataActionPayload;
+        payload = (await dispatch(initMapData({ queryData: EMPTY_QUERY_DATA })))
+          .payload as InitMapDataActionPayload;
       });
 
       it('should return empty payload', () => {
diff --git a/src/redux/map/map.thunks.ts b/src/redux/map/map.thunks.ts
index e6d8fef456ef2f0bda91c46d99cd7c45347c192e..062328d6d9f3c8e511fd738c8c2c9c2b6546d803 100644
--- a/src/redux/map/map.thunks.ts
+++ b/src/redux/map/map.thunks.ts
@@ -1,4 +1,6 @@
 import { PROJECT_ID } from '@/constants';
+import { QueryData } from '@/types/query';
+import { getUpdatedMapData } from '@/utils/map/getUpdatedMapData';
 import { createAsyncThunk } from '@reduxjs/toolkit';
 import { backgroundsDataSelector } from '../backgrounds/background.selectors';
 import { getAllBackgroundsByProjectId } from '../backgrounds/backgrounds.thunks';
@@ -6,36 +8,49 @@ import { modelsDataSelector } from '../models/models.selectors';
 import { getModels } from '../models/models.thunks';
 import { getAllPublicOverlaysByProjectId } from '../overlays/overlays.thunks';
 import type { AppDispatch, RootState } from '../store';
-import { InitMapDataActionPayload } from './map.types';
+import { InitMapDataActionParams, InitMapDataActionPayload } from './map.types';
 
-const getPayloadFromState = (state: RootState): InitMapDataActionPayload => {
+const getInitMapDataPayload = (
+  state: RootState,
+  queryData: QueryData,
+): InitMapDataActionPayload => {
   const FIRST = 0;
   const models = modelsDataSelector(state);
   const backgrounds = backgroundsDataSelector(state);
-  const modelId = models?.[FIRST]?.idObject;
-  const backgroundId = backgrounds?.[FIRST]?.id;
+  const modelId = queryData?.modelId || models?.[FIRST]?.idObject;
+  const backgroundId = queryData?.backgroundId || backgrounds?.[FIRST]?.id;
+  const model = models.find(({ idObject }) => idObject === modelId);
+  const background = backgrounds.find(({ id }) => id === backgroundId);
+  const position = queryData?.initialPosition;
 
-  if (!modelId || !backgroundId) {
+  if (!model || !background) {
     return {};
   }
 
-  return {
-    modelId,
-    backgroundId,
-  };
+  return getUpdatedMapData({
+    model,
+    background,
+    position: {
+      last: position,
+      initial: position,
+    },
+  });
 };
 
 export const initMapData = createAsyncThunk<
   InitMapDataActionPayload,
-  void,
+  InitMapDataActionParams,
   { dispatch: AppDispatch; state: RootState }
->('map/initMapData', async (_, { dispatch, getState }): Promise<InitMapDataActionPayload> => {
-  await Promise.all([
-    dispatch(getAllBackgroundsByProjectId(PROJECT_ID)),
-    dispatch(getAllPublicOverlaysByProjectId(PROJECT_ID)),
-    dispatch(getModels()),
-  ]);
+>(
+  'map/initMapData',
+  async ({ queryData }, { dispatch, getState }): Promise<InitMapDataActionPayload> => {
+    await Promise.all([
+      dispatch(getAllBackgroundsByProjectId(PROJECT_ID)),
+      dispatch(getAllPublicOverlaysByProjectId(PROJECT_ID)),
+      dispatch(getModels()),
+    ]);
 
-  const state = getState();
-  return getPayloadFromState(state);
-});
+    const state = getState();
+    return getInitMapDataPayload(state, queryData);
+  },
+);
diff --git a/src/redux/map/map.types.ts b/src/redux/map/map.types.ts
index 7051691ed57d56fe19e934c18e8fa020a9f9246e..2f723872a225ef9bf88d73ab60373e00a85cc9a0 100644
--- a/src/redux/map/map.types.ts
+++ b/src/redux/map/map.types.ts
@@ -1,6 +1,7 @@
 import { FetchDataState } from '@/types/fetchDataState';
 import { Point } from '@/types/map';
-import { PayloadAction } from '@reduxjs/toolkit';
+import { QueryData } from '@/types/query';
+import { DeepPartial, PayloadAction } from '@reduxjs/toolkit';
 
 export interface MapSize {
   width: number;
@@ -17,7 +18,10 @@ export type MapData = {
   backgroundId: number;
   overlaysIds: number[];
   size: MapSize;
-  position: Point;
+  position: {
+    initial: Point;
+    last: Point;
+  };
   show: {
     legend: boolean;
     comments: boolean;
@@ -26,14 +30,28 @@ export type MapData = {
 
 export type MapState = FetchDataState<MapData, MapData>;
 
-export type SetMapDataActionPayload = Partial<MapData> | undefined;
+export type SetMapDataActionPayload =
+  | (Omit<Partial<MapData>, 'position' | 'projectId'> & {
+      position?: DeepPartial<MapData['position']>;
+      projectId?: string;
+    })
+  | undefined;
 
 export type SetMapDataAction = PayloadAction<SetMapDataActionPayload>;
 
-export type InitMapDataActionPayload = { modelId: number; backgroundId: number } | object;
+export type InitMapDataActionParams = { queryData: QueryData };
+
+export type InitMapDataActionPayload = SetMapDataActionPayload | object;
 
 export type InitMapDataAction = PayloadAction<SetMapDataAction>;
 
 export type MiddlewareAllowedAction = PayloadAction<
   SetMapDataActionPayload | InitMapDataActionPayload
 >;
+
+export type SetMapDataByQueryDataActionParams = { queryData: QueryData };
+
+export type SetMapDataByQueryDataActionPayload = Pick<
+  MapData,
+  'modelId' | 'backgroundId' | 'position'
+>;
diff --git a/src/redux/map/middleware/map.middleware.ts b/src/redux/map/middleware/map.middleware.ts
index 0221d9f6a2705f68cad2a51b2c1c24c7b2cb7565..a09dc6bcc00004bcced835a767e3f6c4ba63b69d 100644
--- a/src/redux/map/middleware/map.middleware.ts
+++ b/src/redux/map/middleware/map.middleware.ts
@@ -1,8 +1,8 @@
+import { currentBackgroundSelector } from '@/redux/backgrounds/background.selectors';
 import type { AppListenerEffectAPI, AppStartListening } from '@/redux/store';
 import { getUpdatedMapData } from '@/utils/map/getUpdatedMapData';
 import { Action, createListenerMiddleware } from '@reduxjs/toolkit';
 import { setMapData } from '../map.slice';
-import { initMapData } from '../map.thunks';
 import { checkIfIsMapUpdateActionValid } from './checkIfIsMapUpdateActionValid';
 import { getUpdatedModel } from './getUpdatedModel';
 
@@ -22,15 +22,11 @@ export const mapDataMiddlewareListener = async (
     return;
   }
 
-  const updatedMapData = getUpdatedMapData({ model: updatedModel });
+  const background = currentBackgroundSelector(state);
+  const updatedMapData = getUpdatedMapData({ model: updatedModel, background });
   dispatch(setMapData(updatedMapData));
 };
 
-startListening({
-  actionCreator: initMapData.fulfilled,
-  effect: mapDataMiddlewareListener,
-});
-
 startListening({
   type: 'map/setMapData',
   effect: mapDataMiddlewareListener,
diff --git a/src/redux/root/init.selectors.ts b/src/redux/root/init.selectors.ts
index 82176735f129c72fbb1161a73b6ee241f3e6909c..5095956a5633679600d67a86d458ecffb7227ac2 100644
--- a/src/redux/root/init.selectors.ts
+++ b/src/redux/root/init.selectors.ts
@@ -1,5 +1,6 @@
 import { createSelector } from '@reduxjs/toolkit';
 import { backgroundsSelector } from '../backgrounds/background.selectors';
+import { mapSelector } from '../map/map.selectors';
 import { modelsSelector } from '../models/models.selectors';
 import { overlaysSelector } from '../overlays/overlays.selectors';
 import { projectSelector } from '../project/project.selectors';
@@ -11,3 +12,12 @@ export const initDataLoadingInitialized = createSelector(
   overlaysSelector,
   (...selectors) => selectors.every(selector => selector.loading !== 'idle'),
 );
+
+export const initDataLoadingFinished = createSelector(
+  projectSelector,
+  backgroundsSelector,
+  modelsSelector,
+  overlaysSelector,
+  mapSelector,
+  (...selectors) => selectors.every(selector => selector.loading === 'succeeded'),
+);
diff --git a/src/redux/root/query.selectors.ts b/src/redux/root/query.selectors.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e0c3d7850dc2b53a4140a9bf02c335c2c06637f3
--- /dev/null
+++ b/src/redux/root/query.selectors.ts
@@ -0,0 +1,12 @@
+import { QueryDataParams } from '@/types/query';
+import { createSelector } from '@reduxjs/toolkit';
+import { mapDataSelector } from '../map/map.selectors';
+
+export const queryDataParamsSelector = createSelector(
+  mapDataSelector,
+  ({ modelId, backgroundId, position }): QueryDataParams => ({
+    modelId,
+    backgroundId,
+    ...position.last,
+  }),
+);
diff --git a/src/types/query.ts b/src/types/query.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a715a34a3397f9f4bb7b2a3eaa7e82657d7d1463
--- /dev/null
+++ b/src/types/query.ts
@@ -0,0 +1,15 @@
+import { Point } from './map';
+
+export interface QueryData {
+  modelId?: number;
+  backgroundId?: number;
+  initialPosition?: Partial<Point>;
+}
+
+export interface QueryDataParams {
+  modelId?: number;
+  backgroundId?: number;
+  x?: number;
+  y?: number;
+  z?: number;
+}
diff --git a/src/types/utils.ts b/src/types/utils.ts
new file mode 100644
index 0000000000000000000000000000000000000000..969944cb5527ae540324b159f192a8e971271546
--- /dev/null
+++ b/src/types/utils.ts
@@ -0,0 +1,3 @@
+export type WithoutNullableKeys<Type> = {
+  [Key in keyof Type]-?: WithoutNullableKeys<NonNullable<Type[Key]>>;
+};
diff --git a/src/components/SPA/utils/useInitializeStore.test.ts b/src/utils/initialize/useInitializeStore.test.ts
similarity index 100%
rename from src/components/SPA/utils/useInitializeStore.test.ts
rename to src/utils/initialize/useInitializeStore.test.ts
diff --git a/src/components/SPA/utils/useInitializeStore.ts b/src/utils/initialize/useInitializeStore.ts
similarity index 56%
rename from src/components/SPA/utils/useInitializeStore.ts
rename to src/utils/initialize/useInitializeStore.ts
index 7e2fb0af61681ddf2f73cefa1571580fd8b36022..68a6100e2075d8fa3e3a8ce1cb6a1465036e1b1c 100644
--- a/src/components/SPA/utils/useInitializeStore.ts
+++ b/src/utils/initialize/useInitializeStore.ts
@@ -4,26 +4,39 @@ import { initMapData } from '@/redux/map/map.thunks';
 import { getProjectById } from '@/redux/project/project.thunks';
 import { initDataLoadingInitialized } from '@/redux/root/init.selectors';
 import { AppDispatch } from '@/redux/store';
+import { QueryData } from '@/types/query';
+import { useRouter } from 'next/router';
 import { useEffect } from 'react';
 import { useSelector } from 'react-redux';
+import { getQueryData } from '../query-manager/getQueryData';
+
+interface GetInitStoreDataArgs {
+  queryData: QueryData;
+}
 
 /* prettier-ignore */
 export const getInitStoreData =
-  () =>
+  ({ queryData }: GetInitStoreDataArgs) =>
     (dispatch: AppDispatch): void => {
       dispatch(getProjectById(PROJECT_ID));
-      dispatch(initMapData());
+      dispatch(initMapData({ queryData }));
     };
 
 export const useInitializeStore = (): void => {
   const dispatch = useAppDispatch();
   const isInitialized = useSelector(initDataLoadingInitialized);
+  const { query, isReady: isRouterReady } = useRouter();
 
   useEffect(() => {
-    if (isInitialized) {
+    const isQueryReady = query && isRouterReady;
+    if (isInitialized || !isQueryReady) {
       return;
     }
 
-    dispatch(getInitStoreData());
-  }, [dispatch, isInitialized]);
+    dispatch(
+      getInitStoreData({
+        queryData: getQueryData(query),
+      }),
+    );
+  }, [dispatch, query, isInitialized, isRouterReady]);
 };
diff --git a/src/utils/map/getPointOffset.ts b/src/utils/map/getPointOffset.ts
index 9c4e01fe65cbd78846f6396af30c71a2971de7d2..08e60559574b402e40ef3ddbcb37129f5306d856 100644
--- a/src/utils/map/getPointOffset.ts
+++ b/src/utils/map/getPointOffset.ts
@@ -3,7 +3,13 @@ import { VALID_MAP_SIZE_SCHEMA } from '@/constants/map';
 import { MapSize } from '@/redux/map/map.types';
 import { Point } from '@/types/map';
 
-export const getPointOffset = (point: Point, mapSize: MapSize): Point => {
+interface GetPointOffsetResults extends Point {
+  pointOrigin: Point;
+  pointShifted: Point;
+  zoomFactor: number;
+}
+
+export const getPointOffset = (point: Point, mapSize: MapSize): GetPointOffsetResults => {
   // parse throws error if map size may lead to invalid results
   VALID_MAP_SIZE_SCHEMA.parse(mapSize);
 
@@ -21,5 +27,11 @@ export const getPointOffset = (point: Point, mapSize: MapSize): Point => {
     y: point.y / zoomFactor,
   };
 
-  return { x: pointShifted.x - pointOrigin.x, y: pointShifted.y - pointOrigin.y };
+  return {
+    x: pointShifted.x - pointOrigin.x,
+    y: pointShifted.y - pointOrigin.y,
+    pointOrigin,
+    pointShifted,
+    zoomFactor,
+  };
 };
diff --git a/src/utils/map/getUpdatedMapData.test.ts b/src/utils/map/getUpdatedMapData.test.ts
index 5afdbc3c00a37776b425577324036ae739553b07..4e4b28522c80a83c0fd23ac76091125f91af56b1 100644
--- a/src/utils/map/getUpdatedMapData.test.ts
+++ b/src/utils/map/getUpdatedMapData.test.ts
@@ -24,9 +24,16 @@ describe('getUpdatedMapData - util', () => {
           maxZoom: model.maxZoom,
         },
         position: {
-          x: model.width / HALF,
-          y: model.height / HALF,
-          z: DEFAULT_ZOOM,
+          initial: {
+            x: model.width / HALF,
+            y: model.height / HALF,
+            z: DEFAULT_ZOOM,
+          },
+          last: {
+            x: model.width / HALF,
+            y: model.height / HALF,
+            z: DEFAULT_ZOOM,
+          },
         },
       };
 
@@ -53,9 +60,16 @@ describe('getUpdatedMapData - util', () => {
           maxZoom: model.maxZoom,
         },
         position: {
-          x: 0,
-          y: 0,
-          z: DEFAULT_ZOOM,
+          initial: {
+            x: 0,
+            y: 0,
+            z: DEFAULT_ZOOM,
+          },
+          last: {
+            x: 0,
+            y: 0,
+            z: DEFAULT_ZOOM,
+          },
         },
       };
 
@@ -82,9 +96,16 @@ describe('getUpdatedMapData - util', () => {
           maxZoom: model.maxZoom,
         },
         position: {
-          x: 10,
-          y: 10,
-          z: 1,
+          initial: {
+            x: 10,
+            y: 10,
+            z: 1,
+          },
+          last: {
+            x: 10,
+            y: 10,
+            z: 1,
+          },
         },
       };
 
diff --git a/src/utils/map/getUpdatedMapData.ts b/src/utils/map/getUpdatedMapData.ts
index 552e29a64b20d955929f5e99a2233c915aebe9f0..c3ebf2a09e1988249062c119681baa8b4e869944 100644
--- a/src/utils/map/getUpdatedMapData.ts
+++ b/src/utils/map/getUpdatedMapData.ts
@@ -1,27 +1,46 @@
 import { DEFAULT_ZOOM } from '@/constants/map';
-import { MapData } from '@/redux/map/map.types';
-import { MapModel } from '@/types/models';
+import { MAP_DATA_INITIAL_STATE } from '@/redux/map/map.constants';
+import { MapData, SetMapDataActionPayload } from '@/redux/map/map.types';
+import { MapBackground, MapModel } from '@/types/models';
+import { DeepPartial } from '@reduxjs/toolkit';
+import { getPointMerged } from '../object/getPointMerged';
 
 interface GetUpdatedMapDataArgs {
   model: MapModel;
+  position?: DeepPartial<MapData['position']>;
+  background?: MapBackground;
 }
 
-type GetUpdatedMapDataResult = Pick<MapData, 'modelId' | 'size' | 'position'>;
+type GetUpdatedMapDataResult = SetMapDataActionPayload;
 
 const HALF = 2;
 
-export const getUpdatedMapData = ({ model }: GetUpdatedMapDataArgs): GetUpdatedMapDataResult => ({
-  modelId: model.idObject,
-  size: {
-    width: model.width,
-    height: model.height,
-    tileSize: model.tileSize,
-    minZoom: model.minZoom,
-    maxZoom: model.maxZoom,
-  },
-  position: {
+export const getUpdatedMapData = ({
+  model,
+  position,
+  background,
+}: GetUpdatedMapDataArgs): GetUpdatedMapDataResult => {
+  const defaultPosition = {
     x: model.defaultCenterX ?? model.width / HALF,
     y: model.defaultCenterY ?? model.height / HALF,
     z: model.defaultZoomLevel ?? DEFAULT_ZOOM,
-  },
-});
+  };
+
+  const mergedPosition = getPointMerged(position?.initial || {}, defaultPosition);
+
+  return {
+    modelId: model.idObject,
+    backgroundId: background?.id || MAP_DATA_INITIAL_STATE.backgroundId,
+    size: {
+      width: model.width,
+      height: model.height,
+      tileSize: model.tileSize,
+      minZoom: model.minZoom,
+      maxZoom: model.maxZoom,
+    },
+    position: {
+      initial: mergedPosition,
+      last: mergedPosition,
+    },
+  };
+};
diff --git a/src/utils/map/latLngToPoint.ts b/src/utils/map/latLngToPoint.ts
new file mode 100644
index 0000000000000000000000000000000000000000..aa799201ba057e7cfaf5bca95655e6216dff9abf
--- /dev/null
+++ b/src/utils/map/latLngToPoint.ts
@@ -0,0 +1,40 @@
+/* eslint-disable no-magic-numbers */
+import { DEFAULT_CENTER_POINT } from '@/constants/map';
+import { MapSize } from '@/redux/map/map.types';
+import { LatLng, Point } from '@/types/map';
+import { boundNumber } from '../number/boundNumber';
+import { degreesToRadians } from '../number/degreesToRadians';
+import { getPointOffset } from './getPointOffset';
+
+const FULL_CIRCLE_DEGREES = 360;
+const SIN_Y_LIMIT = 0.9999;
+
+interface Options {
+  rounded?: boolean;
+}
+
+export const latLngToPoint = (
+  [lat, lng]: LatLng,
+  mapSize: MapSize,
+  options: Options = {},
+): Point => {
+  const { pointOrigin, zoomFactor } = getPointOffset(DEFAULT_CENTER_POINT, mapSize);
+  const pixelsPerLonDegree = mapSize.tileSize / FULL_CIRCLE_DEGREES;
+  const pixelsPerLonRadian = mapSize.tileSize / (2 * Math.PI);
+  const sinY = boundNumber(Math.sin(degreesToRadians(lat)), -SIN_Y_LIMIT, SIN_Y_LIMIT);
+
+  const point = {
+    x: pointOrigin.x + lng * pixelsPerLonDegree,
+    y: pointOrigin.y + 0.5 * Math.log((1 + sinY) / (1 - sinY)) * -pixelsPerLonRadian,
+  };
+
+  const getFinalPointValue = (pointValue: number): number => {
+    const pointValueFactored = pointValue * zoomFactor;
+    return options?.rounded ? Math.round(pointValueFactored) : pointValueFactored;
+  };
+
+  return {
+    x: getFinalPointValue(point.x),
+    y: getFinalPointValue(point.y),
+  };
+};
diff --git a/src/utils/map/pointToLatLng.test.ts b/src/utils/map/pointToLatLng.test.ts
index 2625526a1ff11a741bffa34ed336df6b0863d67d..8c34d284fe32a5f1725b2572fdd0d78a870412d2 100644
--- a/src/utils/map/pointToLatLng.test.ts
+++ b/src/utils/map/pointToLatLng.test.ts
@@ -1,6 +1,6 @@
 /* eslint-disable no-magic-numbers */
 import { LATLNG_FALLBACK } from '@/constants/map';
-import { pointToLatLng } from './pointToLatLng';
+import { pointToLngLat } from './pointToLatLng';
 
 describe('pointToLatLng - util', () => {
   describe('when mapSize arg is undefined', () => {
@@ -12,7 +12,7 @@ describe('pointToLatLng - util', () => {
     const mapSizeUndefined = undefined;
 
     it('should return fallback value', () => {
-      expect(pointToLatLng(validPoint, mapSizeUndefined)).toBe(LATLNG_FALLBACK);
+      expect(pointToLngLat(validPoint, mapSizeUndefined)).toBe(LATLNG_FALLBACK);
     });
   });
 
@@ -34,7 +34,7 @@ describe('pointToLatLng - util', () => {
     it('should return fallback value', () => {
       const logger = jest.spyOn(console, 'error').mockImplementation(() => {});
 
-      expect(pointToLatLng(validPoint, invalidMapSize)).toBe(LATLNG_FALLBACK);
+      expect(pointToLngLat(validPoint, invalidMapSize)).toBe(LATLNG_FALLBACK);
       // TODO: need to rething way of handling parsing errors, for now let's leave it to console.log
       // eslint-disable-next-line no-console
       expect(logger).toBeCalledTimes(1);
@@ -58,7 +58,7 @@ describe('pointToLatLng - util', () => {
     const results = [-270, 0];
 
     it('should return valid lat lng value', () => {
-      expect(pointToLatLng(validPoint, validMapSize)).toStrictEqual(results);
+      expect(pointToLngLat(validPoint, validMapSize)).toStrictEqual(results);
     });
   });
 });
diff --git a/src/utils/map/pointToLatLng.ts b/src/utils/map/pointToLatLng.ts
index d6d82a4e2551b1f9e57eb5da3924ced0258bc0cf..cf8a6e58e8bad0bee61593660b332c9b8a95760c 100644
--- a/src/utils/map/pointToLatLng.ts
+++ b/src/utils/map/pointToLatLng.ts
@@ -19,7 +19,7 @@ const getIsMapSizeValid = (mapSize?: MapSize): boolean => {
   return parseResult.success;
 };
 
-export const pointToLatLng = (point: Point, mapSize?: MapSize): LatLng => {
+export const pointToLngLat = (point: Point, mapSize?: MapSize): LatLng => {
   const isMapSizeValid = getIsMapSizeValid(mapSize);
   if (!isMapSizeValid || !mapSize) {
     return LATLNG_FALLBACK;
diff --git a/src/utils/map/usePointToProjection.test.tsx b/src/utils/map/usePointToProjection.test.tsx
index a647f1fc71ce15f531001e3ccf0cce753b150638..b3659e7aea7c613a186c23e36fe728cd4c152586 100644
--- a/src/utils/map/usePointToProjection.test.tsx
+++ b/src/utils/map/usePointToProjection.test.tsx
@@ -1,8 +1,6 @@
 import mapReducer, { setMapData } from '@/redux/map/map.slice';
 /* eslint-disable no-magic-numbers */
-import { LATLNG_FALLBACK } from '@/constants/map';
 import { act, renderHook } from '@testing-library/react';
-import { fromLonLat } from 'ol/proj';
 import { getReduxWrapperUsingSliceReducer } from '../testing/getReduxWrapperUsingSliceReducer';
 import { usePointToProjection } from './usePointToProjection';
 
@@ -29,7 +27,7 @@ describe('usePointToProjection - util', () => {
       const {
         result: { current: pointToProjection },
       } = renderHook(usePointToProjection, { wrapper: Wrapper });
-      expect(pointToProjection(validPoint)).toStrictEqual(fromLonLat(LATLNG_FALLBACK));
+      expect(pointToProjection(validPoint)).toStrictEqual([0, -0]);
     });
   });
 
@@ -60,7 +58,7 @@ describe('usePointToProjection - util', () => {
       const {
         result: { current: pointToProjection },
       } = renderHook(usePointToProjection, { wrapper: Wrapper });
-      expect(pointToProjection(validPoint)).toStrictEqual(fromLonLat(LATLNG_FALLBACK));
+      expect(pointToProjection(validPoint)).toStrictEqual([0, -0]);
     });
   });
 
@@ -78,7 +76,7 @@ describe('usePointToProjection - util', () => {
       maxZoom: 10,
     };
 
-    const results = [180337575.0851032, -180337344.38930294];
+    const results = [180337575, -180337344];
 
     it('should return valid lat lng value on function call', () => {
       act(() => {
diff --git a/src/utils/map/usePointToProjection.ts b/src/utils/map/usePointToProjection.ts
index 281a33bb55556dfcf80ef4433b8b3eda5cf92855..d0b0066b98d5f73abc92c4cca76dadf86e439ead 100644
--- a/src/utils/map/usePointToProjection.ts
+++ b/src/utils/map/usePointToProjection.ts
@@ -5,7 +5,7 @@ import { Coordinate } from 'ol/coordinate';
 import { fromLonLat } from 'ol/proj';
 import { useCallback } from 'react';
 import { useSelector } from 'react-redux';
-import { pointToLatLng } from './pointToLatLng';
+import { pointToLngLat } from './pointToLatLng';
 
 type UsePointToProjectionResult = (point: Point) => Coordinate;
 
@@ -16,11 +16,12 @@ export const usePointToProjection: UsePointToProjection = () => {
 
   const pointToProjection = useCallback(
     (point: Point): Coordinate => {
-      const [lng, lat] = pointToLatLng(point, mapSize);
+      const [lng, lat] = pointToLngLat(point, mapSize);
       const projection = fromLonLat([lng, lat]);
-      const isValid = !projection.some(v => Number.isNaN(v));
+      const projectionRounded = projection.map(v => Math.round(v));
+      const isValid = !projectionRounded.some(v => Number.isNaN(v));
 
-      return isValid ? projection : LATLNG_FALLBACK;
+      return isValid ? projectionRounded : LATLNG_FALLBACK;
     },
     [mapSize],
   );
diff --git a/src/utils/number/boundNumber.ts b/src/utils/number/boundNumber.ts
new file mode 100644
index 0000000000000000000000000000000000000000..abad3552fc7463f45ea5dd998d30556127dd7ed9
--- /dev/null
+++ b/src/utils/number/boundNumber.ts
@@ -0,0 +1,4 @@
+export const boundNumber = (value: number, minVal?: number, maxVal?: number): number => {
+  const valueBoundedMax = Math.max(value, minVal || value);
+  return Math.min(valueBoundedMax, maxVal || value);
+};
diff --git a/src/utils/number/degreesToRadians.ts b/src/utils/number/degreesToRadians.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9b413ed22435a48f8956da1db1e0798ad9fbb857
--- /dev/null
+++ b/src/utils/number/degreesToRadians.ts
@@ -0,0 +1,5 @@
+const HALF_CIRCLE_DEGREES = 180;
+
+export const degreesToRadians = (deg: number): number => {
+  return deg * (Math.PI / HALF_CIRCLE_DEGREES);
+};
diff --git a/src/utils/object/getPointMerged.ts b/src/utils/object/getPointMerged.ts
new file mode 100644
index 0000000000000000000000000000000000000000..347655b49a4c6db03a1830b14bd6737bad3d4504
--- /dev/null
+++ b/src/utils/object/getPointMerged.ts
@@ -0,0 +1,7 @@
+import { Point } from '@/types/map';
+
+export const getPointMerged = (primaryPoint: Partial<Point>, secondaryPoint: Point): Point => ({
+  x: primaryPoint.x ?? secondaryPoint.x,
+  y: primaryPoint.y ?? secondaryPoint.y,
+  z: primaryPoint.z ?? secondaryPoint.z,
+});
diff --git a/src/utils/object/getTruthyObjectOrUndefined.ts b/src/utils/object/getTruthyObjectOrUndefined.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e8fe8153bda70f679dfec460bc9c3b9b006c912e
--- /dev/null
+++ b/src/utils/object/getTruthyObjectOrUndefined.ts
@@ -0,0 +1,11 @@
+import { WithoutNullableKeys } from '@/types/utils';
+
+export const getTruthyObjectOrUndefined = <I extends object>(
+  obj: I,
+): WithoutNullableKeys<I> | undefined => {
+  const isAllValuesTruthy = Object.entries(obj).every(
+    ([, value]) => value !== null && value !== undefined,
+  );
+
+  return isAllValuesTruthy ? (obj as WithoutNullableKeys<I>) : undefined;
+};
diff --git a/src/utils/query-manager/getQueryData.ts b/src/utils/query-manager/getQueryData.ts
new file mode 100644
index 0000000000000000000000000000000000000000..762118e6b445e628b92624cb9a53f5269ad5e352
--- /dev/null
+++ b/src/utils/query-manager/getQueryData.ts
@@ -0,0 +1,24 @@
+import { QueryData } from '@/types/query';
+import { ParsedUrlQuery } from 'querystring';
+
+/* prettier-ignore */
+const getQueryFieldNumberCurry =
+  (query: ParsedUrlQuery) =>
+    (key: string): number | undefined =>
+      parseInt(`${query?.[key]}`, 10) || undefined;
+
+export const getQueryData = (query: ParsedUrlQuery): QueryData => {
+  const getQueryFieldNumber = getQueryFieldNumberCurry(query);
+
+  const initialPosition = {
+    x: getQueryFieldNumber('x'),
+    y: getQueryFieldNumber('y'),
+    z: getQueryFieldNumber('z'),
+  };
+
+  return {
+    modelId: getQueryFieldNumber('modelId'),
+    backgroundId: getQueryFieldNumber('backgroundId'),
+    initialPosition,
+  };
+};
diff --git a/src/utils/query-manager/useReduxBusQueryManager.ts b/src/utils/query-manager/useReduxBusQueryManager.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4ad04c417f2044f420782544930cffada60388b9
--- /dev/null
+++ b/src/utils/query-manager/useReduxBusQueryManager.ts
@@ -0,0 +1,38 @@
+import { queryDataParamsSelector } from '@/redux/root/query.selectors';
+import { useRouter } from 'next/router';
+import { useCallback, useEffect } from 'react';
+import { useSelector } from 'react-redux';
+import { initDataLoadingFinished } from '../../redux/root/init.selectors';
+
+export const useReduxBusQueryManager = (): void => {
+  const router = useRouter();
+  const queryData = useSelector(queryDataParamsSelector);
+  const isDataLoaded = useSelector(initDataLoadingFinished);
+
+  const handleChangeQuery = useCallback(
+    () =>
+      router.replace(
+        {
+          query: {
+            ...router.query,
+            ...queryData,
+          },
+        },
+        undefined,
+        {
+          shallow: true,
+        },
+      ),
+    // router is not an stable reference
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+    [queryData],
+  );
+
+  useEffect(() => {
+    if (!isDataLoaded) {
+      return;
+    }
+
+    handleChangeQuery();
+  }, [handleChangeQuery, isDataLoaded]);
+};
diff --git a/yarn.lock b/yarn.lock
index c865fa7436c7be819c80651b30e867e2502d5574..9f44645148c1bb09e68044a8d3a46aa3c4b2d56c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6045,7 +6045,7 @@
     "react-is" "^18.0.0"
     "use-sync-external-store" "^1.0.0"
 
-"react@^16.3.2 || ^17.0.0 || ^18.0.0", "react@^16.8 || ^17.0 || ^18.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0-0 || ^17.0.0 || ^18.0.0", "react@^16.9.0 || ^17.0.0 || ^18", "react@^18.0.0", "react@^18.2.0", "react@>= 16.8.0 || 17.x.x || ^18.0.0-0", "react@>=17.0.0", "react@18.2.0":
+"react@^16.3.2 || ^17.0.0 || ^18.0.0", "react@^16.8 || ^17.0 || ^18.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0-0 || ^17.0.0 || ^18.0.0", "react@^16.9.0 || ^17.0.0 || ^18", "react@^18.0.0", "react@^18.2.0", "react@>= 16.8.0 || 17.x.x || ^18.0.0-0", "react@>=16.8.0", "react@>=17.0.0", "react@18.2.0":
   "integrity" "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ=="
   "resolved" "https://registry.npmjs.org/react/-/react-18.2.0.tgz"
   "version" "18.2.0"
@@ -6890,6 +6890,11 @@
   "resolved" "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz"
   "version" "1.0.3"
 
+"ts-deepmerge@^6.2.0":
+  "integrity" "sha512-2qxI/FZVDPbzh63GwWIZYE7daWKtwXZYuyc8YNq0iTmMUwn4mL0jRLsp6hfFlgbdRSR4x2ppe+E86FnvEpN7Nw=="
+  "resolved" "https://registry.npmjs.org/ts-deepmerge/-/ts-deepmerge-6.2.0.tgz"
+  "version" "6.2.0"
+
 "ts-interface-checker@^0.1.9":
   "integrity" "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="
   "resolved" "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz"
@@ -7092,6 +7097,11 @@
     "querystringify" "^2.1.1"
     "requires-port" "^1.0.0"
 
+"use-debounce@^9.0.4":
+  "integrity" "sha512-6X8H/mikbrt0XE8e+JXRtZ8yYVvKkdYRfmIhWZYsP8rcNs9hk3APV8Ua2mFkKRLcJKVdnX2/Vwrmg2GWKUQEaQ=="
+  "resolved" "https://registry.npmjs.org/use-debounce/-/use-debounce-9.0.4.tgz"
+  "version" "9.0.4"
+
 "use-sync-external-store@^1.0.0":
   "integrity" "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA=="
   "resolved" "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz"