diff --git a/src/components/FunctionalArea/NavBar/NavBar.component.test.tsx b/src/components/FunctionalArea/NavBar/NavBar.component.test.tsx
index c5bff4c3eade40bfea3f82240ff36741467ab2e9..08d6c36c21ccbd80c7ccba9280e367952c96f99d 100644
--- a/src/components/FunctionalArea/NavBar/NavBar.component.test.tsx
+++ b/src/components/FunctionalArea/NavBar/NavBar.component.test.tsx
@@ -1,13 +1,10 @@
-import drawerReducer from '@/redux/drawer/drawer.slice';
-import type { DrawerState } from '@/redux/drawer/drawer.types';
-import { ToolkitStoreWithSingleSlice } from '@/utils/createStoreInstanceUsingSliceReducer';
-import { getReduxWrapperUsingSliceReducer } from '@/utils/testing/getReduxWrapperUsingSliceReducer';
 import { render, screen } from '@testing-library/react';
+import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore';
+import { StoreType } from '@/redux/store';
 import { NavBar } from './NavBar.component';
 
-const renderComponent = (): { store: ToolkitStoreWithSingleSlice<DrawerState> } => {
-  const { Wrapper, store } = getReduxWrapperUsingSliceReducer('drawer', drawerReducer);
-
+const renderComponent = (): { store: StoreType } => {
+  const { Wrapper, store } = getReduxWrapperWithStore();
   return (
     render(
       <Wrapper>
diff --git a/src/components/FunctionalArea/NavBar/NavBar.component.tsx b/src/components/FunctionalArea/NavBar/NavBar.component.tsx
index 6631b8004159cc2793eb371fd775672e22209904..7eb7b2b0f556a4f4fe4e7a805ed00a916a97cd8a 100644
--- a/src/components/FunctionalArea/NavBar/NavBar.component.tsx
+++ b/src/components/FunctionalArea/NavBar/NavBar.component.tsx
@@ -11,11 +11,16 @@ import { store } from '@/redux/store';
 import Image from 'next/image';
 import { useAppSelector } from '@/redux/hooks/useAppSelector';
 import { projectIdSelector } from '@/redux/project/project.selectors';
+import { Switch } from '@/shared/Switch';
+import { currentModelIdSelector, vectorRenderingSelector } from '@/redux/models/models.selectors';
+import { setModelVectorRendering } from '@/redux/models/models.slice';
 
 export const NavBar = (): JSX.Element => {
   const dispatch = useAppDispatch();
 
   const projectId = useAppSelector(projectIdSelector);
+  const vectorRendering = useAppSelector(vectorRenderingSelector);
+  const currentModelId = useAppSelector(currentModelIdSelector);
 
   const toggleDrawerInfo = (): void => {
     if (store.getState().drawer.isOpen && store.getState().drawer.drawerName === 'project-info') {
@@ -77,7 +82,15 @@ export const NavBar = (): JSX.Element => {
           <IconButton icon="legend" onClick={toggleDrawerLegend} title="Legend" />
         </div>
       </div>
-
+      <div className="flex flex-col items-center gap-[10px] text-center text-[12px]">
+        <span>Vector rendering</span>
+        <Switch
+          isChecked={vectorRendering}
+          onToggle={value =>
+            dispatch(setModelVectorRendering({ vectorRendering: value, mapId: currentModelId }))
+          }
+        />
+      </div>
       <div className="flex flex-col items-center gap-[20px]" data-testid="nav-logos-and-powered-by">
         <Image
           className="rounded rounded-e rounded-s bg-white-pearl pb-[7px]"
diff --git a/src/components/Map/Map.component.tsx b/src/components/Map/Map.component.tsx
index 9b032ffe2b6b4d24336e184b405218644fe2a6ff..67d4d216c793458cefe6cd83af70a72989112876 100644
--- a/src/components/Map/Map.component.tsx
+++ b/src/components/Map/Map.component.tsx
@@ -1,23 +1,18 @@
 /* eslint-disable no-magic-numbers */
 import { Drawer } from '@/components/Map/Drawer';
 import { Legend } from '@/components/Map/Legend';
-import { MapVectorViewer } from '@/components/Map/MapVectorViewer';
 import { MapViewer } from '@/components/Map/MapViewer';
-import { useAppSelector } from '@/redux/hooks/useAppSelector';
-import { vectorRenderingSelector } from '@/redux/models/models.selectors';
 import { MapAdditionalActions } from './MapAdditionalActions';
 import { MapAdditionalOptions } from './MapAdditionalOptions';
 import { PluginsDrawer } from './PluginsDrawer';
 
 export const Map = (): JSX.Element => {
-  const vectorRendering = useAppSelector(vectorRenderingSelector);
   return (
     <div
       className="relative z-0 h-screen w-full overflow-hidden bg-black"
       data-testid="map-container"
     >
-      {!vectorRendering && <MapViewer />}
-      {vectorRendering && <MapVectorViewer />}
+      <MapViewer />
       <MapAdditionalOptions />
       <Drawer />
       <PluginsDrawer />
diff --git a/src/components/Map/MapVectorViewer/MapVectorViewer.component.test.tsx b/src/components/Map/MapVectorViewer/MapVectorViewer.component.test.tsx
deleted file mode 100644
index fd005a8f54795b13441537a69b8557286940fa43..0000000000000000000000000000000000000000
--- a/src/components/Map/MapVectorViewer/MapVectorViewer.component.test.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-import { render, screen } from '@testing-library/react';
-import { StoreType } from '@/redux/store';
-import { initialMapStateFixture } from '@/redux/map/map.fixtures';
-import { BACKGROUND_INITIAL_STATE_MOCK } from '@/redux/backgrounds/background.mock';
-import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore';
-import { MapVectorViewer } from './MapVectorViewer.component';
-import { MAP_VECTOR_VIEWER_ROLE } from './MapVectorViewer.constants';
-
-const renderComponent = (): { store: StoreType } => {
-  const { Wrapper, store } = getReduxWrapperWithStore({
-    map: initialMapStateFixture,
-    backgrounds: BACKGROUND_INITIAL_STATE_MOCK,
-  });
-
-  return (
-    render(
-      <Wrapper>
-        <MapVectorViewer />
-      </Wrapper>,
-    ),
-    {
-      store,
-    }
-  );
-};
-
-describe('MapVectorViewer - component', () => {
-  it('should render component container', () => {
-    renderComponent();
-
-    expect(screen.getByRole(MAP_VECTOR_VIEWER_ROLE)).toBeInTheDocument();
-  });
-
-  it('should render openlayers map inside the component', () => {
-    renderComponent();
-
-    const FIRST_NODE = 0;
-
-    expect(screen.getByRole(MAP_VECTOR_VIEWER_ROLE).childNodes[FIRST_NODE]).toHaveClass(
-      'ol-viewport',
-    );
-  });
-});
diff --git a/src/components/Map/MapVectorViewer/MapVectorViewer.component.tsx b/src/components/Map/MapVectorViewer/MapVectorViewer.component.tsx
deleted file mode 100644
index f58823942aa909afcad442bdb8eccf2cb9c22df7..0000000000000000000000000000000000000000
--- a/src/components/Map/MapVectorViewer/MapVectorViewer.component.tsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import 'ol/ol.css';
-import { useOlMap } from '@/components/Map/MapVectorViewer/utils/useOlMap';
-import { MAP_VECTOR_VIEWER_ROLE } from '@/components/Map/MapVectorViewer/MapVectorViewer.constants';
-
-export const MapVectorViewer = (): JSX.Element => {
-  const { mapRef } = useOlMap();
-  return (
-    <div
-      ref={mapRef}
-      role={MAP_VECTOR_VIEWER_ROLE}
-      className="absolute left-[88px] top-[104px] h-[calc(100%-104px)] w-[calc(100%-88px)] bg-[#e4e2de]"
-    />
-  );
-};
diff --git a/src/components/Map/MapVectorViewer/MapVectorViewer.constants.ts b/src/components/Map/MapVectorViewer/MapVectorViewer.constants.ts
deleted file mode 100644
index b08c13495aa5acdec1306a36e563824382b4f48a..0000000000000000000000000000000000000000
--- a/src/components/Map/MapVectorViewer/MapVectorViewer.constants.ts
+++ /dev/null
@@ -1 +0,0 @@
-export const MAP_VECTOR_VIEWER_ROLE = 'map-vector-viewer';
diff --git a/src/components/Map/MapVectorViewer/MapVectorViewer.types.ts b/src/components/Map/MapVectorViewer/MapVectorViewer.types.ts
deleted file mode 100644
index f6750e5cb1b7db0b38c29ba97fc7ca3c5cbd7b26..0000000000000000000000000000000000000000
--- a/src/components/Map/MapVectorViewer/MapVectorViewer.types.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import View from 'ol/View';
-import BaseLayer from 'ol/layer/Base';
-
-export type MapConfig = {
-  view: View;
-  layers: BaseLayer[];
-};
diff --git a/src/components/Map/MapVectorViewer/index.ts b/src/components/Map/MapVectorViewer/index.ts
deleted file mode 100644
index 0c1b0e15bd7527a057bd808fd625815b34022500..0000000000000000000000000000000000000000
--- a/src/components/Map/MapVectorViewer/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { MapVectorViewer } from './MapVectorViewer.component';
diff --git a/src/components/Map/MapVectorViewer/utils/config/reactionsLayer/useOlMapReactionsLayer.test.ts b/src/components/Map/MapVectorViewer/utils/config/reactionsLayer/useOlMapReactionsLayer.test.ts
deleted file mode 100644
index 286f831e48e9c7c1e6b832daaeadc2bfc4752d2d..0000000000000000000000000000000000000000
--- a/src/components/Map/MapVectorViewer/utils/config/reactionsLayer/useOlMapReactionsLayer.test.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore';
-import { renderHook } from '@testing-library/react';
-import VectorLayer from 'ol/layer/Vector';
-import { useOlMapReactionsLayer } from '@/components/Map/MapVectorViewer/utils/config/reactionsLayer/useOlMapReactionsLayer';
-
-describe('useOlMapReactionsLayer - util', () => {
-  const { Wrapper } = getReduxWrapperWithStore();
-
-  it('should return VectorLayer', () => {
-    const { result } = renderHook(() => useOlMapReactionsLayer(), {
-      wrapper: Wrapper,
-    });
-
-    expect(result.current).toBeInstanceOf(VectorLayer);
-    expect(result.current.getSourceState()).toBe('ready');
-  });
-});
diff --git a/src/components/Map/MapVectorViewer/utils/config/reactionsLayer/useOlMapReactionsLayer.ts b/src/components/Map/MapVectorViewer/utils/config/reactionsLayer/useOlMapReactionsLayer.ts
deleted file mode 100644
index 3f77f5c506f79fa1e70a757812661db21a760e8c..0000000000000000000000000000000000000000
--- a/src/components/Map/MapVectorViewer/utils/config/reactionsLayer/useOlMapReactionsLayer.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-/* eslint-disable no-magic-numbers */
-import { Feature } from 'ol';
-import VectorLayer from 'ol/layer/Vector';
-import VectorSource from 'ol/source/Vector';
-import { useMemo } from 'react';
-import BasePolygon from '@/components/Map/MapVectorViewer/utils/shapes/BasePolygon';
-import Polygon from 'ol/geom/Polygon';
-import { usePointToProjection } from '@/utils/map/usePointToProjection';
-
-export const useOlMapReactionsLayer = (): VectorLayer<VectorSource<Feature<Polygon>>> => {
-  const pointToProjection = usePointToProjection();
-  const basePolygon = new BasePolygon({
-    points: [
-      pointToProjection({ x: 100, y: 100 }),
-      pointToProjection({ x: 188, y: 100 }),
-      pointToProjection({ x: 210, y: 200 }),
-      pointToProjection({ x: 100, y: 200 }),
-    ],
-  });
-
-  const basePolygon2 = new BasePolygon({
-    points: [
-      pointToProjection({ x: 500, y: 200 }),
-      pointToProjection({ x: 560, y: 230 }),
-      pointToProjection({ x: 620, y: 200 }),
-      pointToProjection({ x: 620, y: 250 }),
-      pointToProjection({ x: 560, y: 280 }),
-      pointToProjection({ x: 500, y: 250 }),
-    ],
-    fill: '#CCFFCC',
-    text: 'Test',
-  });
-
-  const vectorSource = useMemo(() => {
-    return new VectorSource({
-      features: [basePolygon.polygonFeature, basePolygon2.polygonFeature],
-    });
-  }, [basePolygon.polygonFeature, basePolygon2.polygonFeature]);
-
-  return useMemo(
-    () =>
-      new VectorLayer({
-        source: vectorSource,
-      }),
-    [vectorSource],
-  );
-};
diff --git a/src/components/Map/MapVectorViewer/utils/config/useOlMapLayers.ts b/src/components/Map/MapVectorViewer/utils/config/useOlMapLayers.ts
deleted file mode 100644
index 66c0143687f2ae615f479ee597620618e9e4d335..0000000000000000000000000000000000000000
--- a/src/components/Map/MapVectorViewer/utils/config/useOlMapLayers.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable no-magic-numbers */
-import { MapInstance } from '@/types/map';
-import { useEffect } from 'react';
-import { useOlMapWhiteCardLayer } from '@/components/Map/MapVectorViewer/utils/config/useOlMapWhiteCardLayer';
-import { MapConfig } from '../../MapVectorViewer.types';
-import { useOlMapReactionsLayer } from './reactionsLayer/useOlMapReactionsLayer';
-
-interface UseOlMapLayersInput {
-  mapInstance: MapInstance;
-}
-
-export const useOlMapLayers = ({ mapInstance }: UseOlMapLayersInput): MapConfig['layers'] => {
-  const reactionsLayer = useOlMapReactionsLayer();
-  const whiteCardLayer = useOlMapWhiteCardLayer();
-  useEffect(() => {
-    if (!mapInstance) {
-      return;
-    }
-    mapInstance.setLayers([whiteCardLayer, reactionsLayer]);
-  }, [whiteCardLayer, reactionsLayer, mapInstance]);
-
-  return [whiteCardLayer, reactionsLayer];
-};
diff --git a/src/components/Map/MapVectorViewer/utils/config/useOlMapView.test.ts b/src/components/Map/MapVectorViewer/utils/config/useOlMapView.test.ts
deleted file mode 100644
index caf22097ec6215693ea94e20156c045083f5ee6d..0000000000000000000000000000000000000000
--- a/src/components/Map/MapVectorViewer/utils/config/useOlMapView.test.ts
+++ /dev/null
@@ -1,116 +0,0 @@
-/* eslint-disable no-magic-numbers */
-import {
-  BACKGROUNDS_MOCK,
-  BACKGROUND_INITIAL_STATE_MOCK,
-} from '@/redux/backgrounds/background.mock';
-import { MAP_DATA_INITIAL_STATE, OPENED_MAPS_INITIAL_STATE } from '@/redux/map/map.constants';
-import { initialMapStateFixture } from '@/redux/map/map.fixtures';
-import { setMapPosition } from '@/redux/map/map.slice';
-import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore';
-import { act, renderHook, waitFor } from '@testing-library/react';
-import { View } from 'ol';
-import Map from 'ol/Map';
-import React from 'react';
-import { useOlMap } from '@/components/Map/MapVectorViewer/utils/useOlMap';
-import { useOlMapView } from '@/components/Map/MapVectorViewer/utils/config/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 } = getReduxWrapperWithStore({
-      map: initialMapStateFixture,
-      backgrounds: { ...BACKGROUND_INITIAL_STATE_MOCK, data: BACKGROUNDS_MOCK },
-    });
-
-    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_TWICE = 2;
-
-    await act(() => {
-      store.dispatch(
-        setMapPosition({
-          x: 0,
-          y: 0,
-        }),
-      );
-    });
-
-    renderHook(() => useOlMapView({ mapInstance: hohResult.current.mapInstance }), {
-      wrapper: Wrapper,
-    });
-
-    await waitFor(() => expect(setViewSpy).toBeCalledTimes(CALLED_TWICE));
-  });
-
-  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: 128,
-              y: 128,
-            },
-            last: {
-              x: 128,
-              y: 128,
-            },
-          },
-        },
-        loading: 'idle',
-        error: {
-          name: '',
-          message: '',
-        },
-        openedMaps: OPENED_MAPS_INITIAL_STATE,
-      },
-      backgrounds: BACKGROUND_INITIAL_STATE_MOCK,
-    });
-    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/MapVectorViewer/utils/config/useOlMapView.ts b/src/components/Map/MapVectorViewer/utils/config/useOlMapView.ts
deleted file mode 100644
index 4d0669133b0c6d879948683f45d16a2c5dfd81e7..0000000000000000000000000000000000000000
--- a/src/components/Map/MapVectorViewer/utils/config/useOlMapView.ts
+++ /dev/null
@@ -1,77 +0,0 @@
-/* eslint-disable no-magic-numbers */
-import { EXTENT_PADDING_MULTIPLICATOR, OPTIONS } from '@/constants/map';
-import { mapDataInitialPositionSelector, mapDataSizeSelector } from '@/redux/map/map.selectors';
-import { MapInstance, Point } from '@/types/map';
-import { usePointToProjection } from '@/utils/map/usePointToProjection';
-import { View } from 'ol';
-import { Extent, boundingExtent } from 'ol/extent';
-import { useEffect, useMemo } from 'react';
-import { useSelector } from 'react-redux';
-import { MapConfig } from '@/components/Map/MapVectorViewer/MapVectorViewer.types';
-
-interface UseOlMapViewInput {
-  mapInstance: MapInstance;
-}
-
-export const useOlMapView = ({ mapInstance }: UseOlMapViewInput): MapConfig['view'] => {
-  const mapInitialPosition = useSelector(mapDataInitialPositionSelector);
-  const mapSize = useSelector(mapDataSizeSelector);
-  const pointToProjection = usePointToProjection();
-
-  const extent = useMemo((): Extent => {
-    const extentPadding = {
-      horizontal: mapSize.width * EXTENT_PADDING_MULTIPLICATOR,
-      vertical: mapSize.height * EXTENT_PADDING_MULTIPLICATOR,
-    };
-
-    const topLeftPoint: Point = {
-      x: mapSize.width + extentPadding.horizontal,
-      y: mapSize.height + extentPadding.vertical,
-    };
-
-    const bottomRightPoint: Point = {
-      x: -extentPadding.horizontal,
-      y: -extentPadding.vertical,
-    };
-
-    return boundingExtent([topLeftPoint, bottomRightPoint].map(pointToProjection));
-  }, [pointToProjection, mapSize]);
-
-  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,
-      maxZoom: mapSize.maxZoom,
-      minZoom: mapSize.minZoom,
-      extent,
-    }),
-    [mapInitialPosition.z, mapSize.maxZoom, mapSize.minZoom, center, extent],
-  );
-
-  const view = useMemo(() => new View(viewConfig), [viewConfig]);
-
-  useEffect(() => {
-    if (!mapInstance) {
-      return;
-    }
-
-    mapInstance.setView(view);
-  }, [view, mapInstance]);
-
-  return view;
-};
diff --git a/src/components/Map/MapVectorViewer/utils/listeners/onMapPositionChange.test.ts b/src/components/Map/MapVectorViewer/utils/listeners/onMapPositionChange.test.ts
deleted file mode 100644
index 6b6df6372b3dc19fc2f49bf51334f1ec129ff34e..0000000000000000000000000000000000000000
--- a/src/components/Map/MapVectorViewer/utils/listeners/onMapPositionChange.test.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-/* eslint-disable no-magic-numbers */
-import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
-import { mapDataSelector } from '@/redux/map/map.selectors';
-import { MapSize } from '@/redux/map/map.types';
-import { Point } from '@/types/map';
-import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore';
-import { renderHook } from '@testing-library/react';
-import { ObjectEvent } from 'openlayers';
-import { onMapPositionChange } from './onMapPositionChange';
-
-const getEvent = (targetValues: ObjectEvent['target']['values_']): ObjectEvent =>
-  ({
-    target: {
-      values_: targetValues,
-    },
-  }) as unknown as ObjectEvent;
-
-describe('onMapPositionChange - util', () => {
-  const cases: [MapSize, ObjectEvent['target']['values_'], Point][] = [
-    [
-      {
-        width: 26779.25,
-        height: 13503,
-        tileSize: 256,
-        minZoom: 2,
-        maxZoom: 9,
-      },
-      {
-        center: [-18320768.57141088, 18421138.0064355],
-        zoom: 6,
-      },
-      {
-        x: 4589,
-        y: 4320,
-        z: 6,
-      },
-    ],
-    [
-      {
-        width: 5170,
-        height: 1535.1097689075634,
-        tileSize: 256,
-        minZoom: 2,
-        maxZoom: 7,
-      },
-      {
-        center: [-17172011.827663105, 18910737.010646995],
-        zoom: 6.68620779943448,
-      },
-      {
-        x: 1479,
-        y: 581,
-        z: 6.68620779943448,
-      },
-    ],
-  ];
-
-  it.each(cases)(
-    'should set map data position to valid one',
-    (mapSize, targetValues, lastPosition) => {
-      const { Wrapper, store } = getReduxWrapperWithStore();
-      const { result } = renderHook(() => useAppDispatch(), { wrapper: Wrapper });
-      const dispatch = result.current;
-      const event = getEvent(targetValues);
-
-      onMapPositionChange(mapSize, dispatch)(event);
-
-      const { position } = mapDataSelector(store.getState());
-      expect(position.last).toMatchObject(lastPosition);
-    },
-  );
-});
diff --git a/src/components/Map/MapVectorViewer/utils/listeners/onMapPositionChange.ts b/src/components/Map/MapVectorViewer/utils/listeners/onMapPositionChange.ts
deleted file mode 100644
index 7102fec7fdecfd1f771295030dd82e30c42bc767..0000000000000000000000000000000000000000
--- a/src/components/Map/MapVectorViewer/utils/listeners/onMapPositionChange.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { setMapPosition } from '@/redux/map/map.slice';
-import { MapSize } from '@/redux/map/map.types';
-import { AppDispatch } from '@/redux/store';
-import { latLngToPoint } from '@/utils/map/latLngToPoint';
-import { toLonLat } from 'ol/proj';
-import { ObjectEvent } from 'openlayers';
-
-/* prettier-ignore */
-export const onMapPositionChange =
-  (mapSize: MapSize, dispatch: AppDispatch) =>
-    (e: ObjectEvent): void => {
-      // eslint-disable-next-line no-underscore-dangle
-      const { center, zoom } = e.target.values_;
-      const [lng, lat] = toLonLat(center);
-      const { x, y } = latLngToPoint([lat, lng], mapSize, { rounded: true });
-
-      dispatch(
-        setMapPosition({
-          x,
-          y,
-          z: zoom,
-        }),
-      );
-    };
diff --git a/src/components/Map/MapVectorViewer/utils/listeners/useOlMapListeners.test.ts b/src/components/Map/MapVectorViewer/utils/listeners/useOlMapListeners.test.ts
deleted file mode 100644
index bae5bcf5a610b45d1762012ed0d132438f03eba2..0000000000000000000000000000000000000000
--- a/src/components/Map/MapVectorViewer/utils/listeners/useOlMapListeners.test.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import { renderHook } from '@testing-library/react';
-import { View } from 'ol';
-import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore';
-import { initialMapDataFixture, openedMapsThreeSubmapsFixture } from '@/redux/map/map.fixtures';
-import * as positionListener from './onMapPositionChange';
-import { useOlMapListeners } from './useOlMapListeners';
-
-jest.mock('./onMapPositionChange', () => ({
-  __esModule: true,
-  onMapPositionChange: jest.fn(),
-}));
-
-jest.mock('use-debounce', () => {
-  return {
-    useDebounce: (): void => {},
-    useDebouncedCallback: (): void => {},
-  };
-});
-
-describe('useOlMapListeners - util', () => {
-  const { Wrapper } = getReduxWrapperWithStore({
-    map: {
-      data: { ...initialMapDataFixture },
-      loading: 'succeeded',
-      error: { message: '', name: '' },
-      openedMaps: openedMapsThreeSubmapsFixture,
-    },
-  });
-
-  beforeEach(() => {
-    jest.clearAllMocks();
-  });
-
-  describe('on change:center view event', () => {
-    it('should run onMapPositionChange event', () => {
-      const CALLED_ONCE = 1;
-      const view = new View();
-
-      renderHook(() => useOlMapListeners({ view }), { wrapper: Wrapper });
-      view.dispatchEvent('change:center');
-
-      expect(positionListener.onMapPositionChange).toBeCalledTimes(CALLED_ONCE);
-    });
-  });
-});
diff --git a/src/components/Map/MapVectorViewer/utils/listeners/useOlMapListeners.ts b/src/components/Map/MapVectorViewer/utils/listeners/useOlMapListeners.ts
deleted file mode 100644
index 4b7348302c41de78e45e10d78d491b15ad7f3924..0000000000000000000000000000000000000000
--- a/src/components/Map/MapVectorViewer/utils/listeners/useOlMapListeners.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { OPTIONS } from '@/constants/map';
-import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
-import { mapDataSizeSelector } from '@/redux/map/map.selectors';
-import { View } from 'ol';
-import { unByKey } from 'ol/Observable';
-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 dispatch = useAppDispatch();
-
-  const handleChangeCenter = useDebouncedCallback(
-    onMapPositionChange(mapSize, dispatch),
-    OPTIONS.queryPersistTime,
-    { leading: false },
-  );
-
-  useEffect(() => {
-    const key = view.on('change:center', handleChangeCenter);
-
-    return () => unByKey(key);
-  }, [view, handleChangeCenter]);
-};
diff --git a/src/components/Map/MapVectorViewer/utils/shapes/BasePolygon.test.ts b/src/components/Map/MapVectorViewer/utils/shapes/BasePolygon.test.ts
deleted file mode 100644
index 50d60cbef84675f97d6ebc4279787a6e60e9c52a..0000000000000000000000000000000000000000
--- a/src/components/Map/MapVectorViewer/utils/shapes/BasePolygon.test.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-/* eslint-disable no-magic-numbers */
-import { Coordinate } from 'ol/coordinate';
-import BasePolygon from '@/components/Map/MapVectorViewer/utils/shapes/BasePolygon';
-import { usePointToProjection, UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
-import Style from 'ol/style/Style';
-import {
-  GetReduxWrapperUsingSliceReducer,
-  getReduxWrapperWithStore,
-} from '@/utils/testing/getReduxWrapperWithStore';
-import { renderHook } from '@testing-library/react';
-import { initialMapStateFixture } from '@/redux/map/map.fixtures';
-
-const getPointToProjection = (
-  wrapper: ReturnType<GetReduxWrapperUsingSliceReducer>['Wrapper'],
-): UsePointToProjectionResult => {
-  const { result: usePointToProjectionHook } = renderHook(() => usePointToProjection(), {
-    wrapper,
-  });
-
-  return usePointToProjectionHook.current;
-};
-
-describe('BasePolygon', () => {
-  const { Wrapper } = getReduxWrapperWithStore({
-    map: initialMapStateFixture,
-  });
-  const pointToProjection = getPointToProjection(Wrapper);
-
-  it('should create a polygon with default styles', () => {
-    const points: Array<Coordinate> = [
-      pointToProjection({ x: 0, y: 0 }),
-      pointToProjection({ x: 10, y: 0 }),
-      pointToProjection({ x: 10, y: 10 }),
-      pointToProjection({ x: 0, y: 10 }),
-    ];
-
-    const polygon = new BasePolygon({ points });
-
-    expect(polygon).toBeInstanceOf(BasePolygon);
-
-    expect(polygon.polygonFeature.getGeometry()?.getCoordinates()).toEqual([points]);
-
-    const style = polygon.polygonFeature.getStyle();
-    if (style instanceof Style) {
-      expect(style.getFill()?.getColor()).toBe('#fff');
-
-      expect(style.getText()?.getText()).toBe('');
-    }
-  });
-
-  it('should create a polygon with custom fill and text', () => {
-    const points: Array<Coordinate> = [
-      pointToProjection({ x: 0, y: 0 }),
-      pointToProjection({ x: 10, y: 0 }),
-      pointToProjection({ x: 10, y: 10 }),
-      pointToProjection({ x: 0, y: 10 }),
-    ];
-
-    const polygon = new BasePolygon({ points, fill: '#ff0000', text: 'Test' });
-    const style = polygon.polygonFeature.getStyle();
-
-    if (style instanceof Style) {
-      expect(style.getFill()?.getColor()).toBe('#ff0000');
-
-      expect(style.getText()?.getText()).toBe('Test');
-    }
-  });
-});
diff --git a/src/components/Map/MapVectorViewer/utils/shapes/BasePolygon.ts b/src/components/Map/MapVectorViewer/utils/shapes/BasePolygon.ts
deleted file mode 100644
index 7e20478e6921cb0b470e186e7577793592ef0f39..0000000000000000000000000000000000000000
--- a/src/components/Map/MapVectorViewer/utils/shapes/BasePolygon.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-/* eslint-disable no-magic-numbers */
-import { Fill, Stroke, Style, Text } from 'ol/style';
-import Feature from 'ol/Feature';
-import { Polygon } from 'ol/geom';
-import { Coordinate } from 'ol/coordinate';
-
-function getPolygonStyle(text: string, fill: string): Style {
-  return new Style({
-    stroke: new Stroke({
-      color: '#000',
-      width: 1,
-    }),
-    fill: new Fill({
-      color: fill,
-    }),
-    text: new Text({
-      text,
-      font: '18px Calibri,sans-serif',
-      fill: new Fill({
-        color: '#000',
-      }),
-      placement: 'point',
-    }),
-  });
-}
-
-export default class BasePolygon {
-  private readonly polygonCoords: Array<Array<Array<number>>>;
-
-  private readonly polygonStyle: Style;
-
-  polygonFeature: Feature<Polygon>;
-
-  constructor({
-    points,
-    fill = '#fff',
-    text = '',
-  }: {
-    points: Array<Coordinate>;
-    fill?: string;
-    text?: string;
-  }) {
-    this.polygonCoords = [points];
-
-    this.polygonFeature = this.getPolygonFeature();
-
-    this.polygonStyle = getPolygonStyle(text, fill);
-
-    this.polygonFeature.setStyle(this.polygonStyle);
-  }
-
-  getPolygonFeature(): Feature<Polygon> {
-    return new Feature({
-      geometry: new Polygon(this.polygonCoords),
-    });
-  }
-}
diff --git a/src/components/Map/MapVectorViewer/utils/useOlMap.test.ts b/src/components/Map/MapVectorViewer/utils/useOlMap.test.ts
deleted file mode 100644
index 79bc1ed1bb956a9ad94c51a635a721d6a1a93f3d..0000000000000000000000000000000000000000
--- a/src/components/Map/MapVectorViewer/utils/useOlMap.test.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-import { renderHook, waitFor } from '@testing-library/react';
-import { Map } from 'ol';
-import React from 'react';
-import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore';
-import { initialMapStateFixture } from '@/redux/map/map.fixtures';
-import { BACKGROUND_INITIAL_STATE_MOCK } from '@/redux/backgrounds/background.mock';
-import { useOlMap } from './useOlMap';
-
-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('useOlMap - util', () => {
-  const { Wrapper } = getReduxWrapperWithStore({
-    map: initialMapStateFixture,
-    backgrounds: BACKGROUND_INITIAL_STATE_MOCK,
-  });
-
-  describe('when initializing', () => {
-    it('should set map instance', async () => {
-      const dummyElement = document.createElement('div');
-      const { result } = renderHook(() => useOlMap({ target: dummyElement }), { wrapper: Wrapper });
-      await waitFor(() => expect(result.current.mapInstance).toBeInstanceOf(Map));
-    });
-
-    it('should render content inside the target element', async () => {
-      const FIRST_NODE = 0;
-      const dummyElement = document.createElement('div');
-      renderHook(() => useOlMap({ target: dummyElement }), { wrapper: Wrapper });
-
-      expect(dummyElement.childNodes[FIRST_NODE]).toHaveClass('ol-viewport');
-    });
-  });
-});
diff --git a/src/components/Map/MapVectorViewer/utils/useOlMap.ts b/src/components/Map/MapVectorViewer/utils/useOlMap.ts
deleted file mode 100644
index 387afc6581c0b963a975df314fd50856fec3c974..0000000000000000000000000000000000000000
--- a/src/components/Map/MapVectorViewer/utils/useOlMap.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-import { MapInstance } from '@/types/map';
-import { useMapInstance } from '@/utils/context/mapInstanceContext';
-import Map from 'ol/Map';
-import { Zoom } from 'ol/control';
-import React, { MutableRefObject, useEffect } from 'react';
-import { useOlMapLayers } from '@/components/Map/MapVectorViewer/utils/config/useOlMapLayers';
-import { useOlMapView } from '@/components/Map/MapVectorViewer/utils/config/useOlMapView';
-import { useOlMapListeners } from '@/components/Map/MapVectorViewer/utils/listeners/useOlMapListeners';
-
-interface UseOlMapInput {
-  target?: HTMLElement;
-}
-interface UseOlMapOutput {
-  mapRef: MutableRefObject<null | HTMLDivElement>;
-  mapInstance: MapInstance;
-}
-
-type UseOlMap = (input?: UseOlMapInput) => UseOlMapOutput;
-
-export const useOlMap: UseOlMap = ({ target } = {}) => {
-  const mapRef = React.useRef<null | HTMLDivElement>(null);
-  const { mapInstance, handleSetMapInstance } = useMapInstance();
-
-  const view = useOlMapView({ mapInstance });
-  useOlMapLayers({ mapInstance });
-  useOlMapListeners({ view });
-
-  useEffect(() => {
-    if (!mapRef.current || mapRef.current.innerHTML !== '') {
-      return;
-    }
-
-    const map = new Map({
-      target: target || mapRef.current,
-    });
-    map.getControls().forEach(mapControl => {
-      if (mapControl instanceof Zoom) {
-        map.removeControl(mapControl);
-      }
-    });
-
-    handleSetMapInstance(map);
-  }, [target, handleSetMapInstance]);
-
-  return {
-    mapRef,
-    mapInstance,
-  };
-};
diff --git a/src/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants.ts b/src/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d038cdf39f083a21b48dad564fcd513094c0adc2
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants.ts
@@ -0,0 +1,11 @@
+import { ColorObject } from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.types';
+
+export const WHITE_COLOR: ColorObject = {
+  alpha: 255,
+  rgb: 16777215,
+};
+
+export const BLACK_COLOR: ColorObject = {
+  alpha: 255,
+  rgb: -16777216,
+};
diff --git a/src/components/Map/MapViewer/MapViewerVector/MapViewerVector.types.ts b/src/components/Map/MapViewer/MapViewerVector/MapViewerVector.types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..78e365e3a1fc14eccc28c28ec737740f1f77519b
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/MapViewerVector.types.ts
@@ -0,0 +1,47 @@
+import View from 'ol/View';
+import BaseLayer from 'ol/layer/Base';
+
+export type MapConfig = {
+  view: View;
+  layers: BaseLayer[];
+};
+
+export type VerticalAlign = 'TOP' | 'MIDDLE' | 'BOTTOM';
+export type HorizontalAlign = 'LEFT' | 'RIGHT' | 'CENTER' | 'END' | 'START';
+
+export type ColorObject = {
+  alpha: number;
+  rgb: number;
+};
+
+export type ShapePoint = {
+  type: string;
+  absoluteX: number;
+  absoluteY: number;
+  relativeX: number;
+  relativeY: number;
+  relativeHeightForX: number | null;
+  relativeWidthForY: number | null;
+};
+
+export type ShapeCurvePoint = {
+  type: string;
+  absoluteX1: number;
+  absoluteY1: number;
+  relativeX1: number;
+  relativeY1: number;
+  relativeHeightForX1: number | null;
+  relativeWidthForY1: number | null;
+  absoluteX2: number;
+  absoluteY2: number;
+  relativeX2: number;
+  relativeY2: number;
+  relativeHeightForX2: number | null;
+  relativeWidthForY2: number | null;
+  absoluteX3: number;
+  absoluteY3: number;
+  relativeX3: number;
+  relativeY3: number;
+  relativeHeightForX3: number | null;
+  relativeWidthForY3: number | null;
+};
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/useOlMapReactionsLayer.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/useOlMapReactionsLayer.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b54a29b43672e2c9e8a34749bf856ade5079e463
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/useOlMapReactionsLayer.test.ts
@@ -0,0 +1,25 @@
+import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore';
+import { renderHook } from '@testing-library/react';
+import VectorLayer from 'ol/layer/Vector';
+import { initialMapStateFixture } from '@/redux/map/map.fixtures';
+import { BACKGROUND_INITIAL_STATE_MOCK } from '@/redux/backgrounds/background.mock';
+import { Map } from 'ol';
+import { useOlMapReactionsLayer } from '@/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/useOlMapReactionsLayer';
+
+describe('useOlMapReactionsLayer - util', () => {
+  it('should return VectorLayer', () => {
+    const { Wrapper } = getReduxWrapperWithStore({
+      map: initialMapStateFixture,
+      backgrounds: BACKGROUND_INITIAL_STATE_MOCK,
+    });
+
+    const dummyElement = document.createElement('div');
+    const mapInstance = new Map({ target: dummyElement });
+    const { result } = renderHook(() => useOlMapReactionsLayer({ mapInstance }), {
+      wrapper: Wrapper,
+    });
+
+    expect(result.current).toBeInstanceOf(VectorLayer);
+    expect(result.current.getSourceState()).toBe('ready');
+  });
+});
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/useOlMapReactionsLayer.ts b/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/useOlMapReactionsLayer.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ba0cb0480e5fdec0ce8942cc6d717ede32e98cd6
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/useOlMapReactionsLayer.ts
@@ -0,0 +1,87 @@
+/* eslint-disable no-magic-numbers */
+import { Feature } from 'ol';
+import VectorLayer from 'ol/layer/Vector';
+import VectorSource from 'ol/source/Vector';
+import { useEffect, useMemo } from 'react';
+import { usePointToProjection } from '@/utils/map/usePointToProjection';
+import CustomMultiPolygon from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/CustomMultiPolygon';
+import { useSelector } from 'react-redux';
+import { shapesSelector } from '@/redux/shapes/shapes.selectors';
+import { MapInstance } from '@/types/map';
+import {
+  HorizontalAlign,
+  VerticalAlign,
+} from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.types';
+import { modelElementsSelector } from '@/redux/modelElements/modelElements.selector';
+import { currentModelIdSelector } from '@/redux/models/models.selectors';
+import { getModelElements } from '@/redux/modelElements/modelElements.thunks';
+import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
+
+export const useOlMapReactionsLayer = ({
+  mapInstance,
+}: {
+  mapInstance: MapInstance;
+}): VectorLayer<VectorSource<Feature>> => {
+  const dispatch = useAppDispatch();
+  const modelElements = useSelector(modelElementsSelector);
+  const currentModelId = useSelector(currentModelIdSelector);
+  useEffect(() => {
+    dispatch(getModelElements(currentModelId));
+  }, [currentModelId, dispatch]);
+
+  const pointToProjection = usePointToProjection();
+  const shapes = useSelector(shapesSelector);
+
+  const elements = useMemo(() => {
+    if (modelElements) {
+      return modelElements.content.map(element => {
+        const shape = shapes.data.find(bioShape => bioShape.sboTerm === element.sboTerm);
+        if (shape) {
+          return new CustomMultiPolygon({
+            shapes: shape.shapes,
+            x: element.x,
+            y: element.y,
+            nameX: element.nameX,
+            nameY: element.nameY,
+            nameHeight: element.nameHeight,
+            nameWidth: element.nameWidth,
+            width: element.width,
+            height: element.height,
+            zIndex: element.z,
+            lineWidth: element.lineWidth,
+            fontColor: element.fontColor,
+            fillColor: element.fillColor,
+            borderColor: element.borderColor,
+            nameVerticalAlign: element.nameVerticalAlign as VerticalAlign,
+            nameHorizontalAlign: element.nameHorizontalAlign as HorizontalAlign,
+            text: element.name,
+            pointToProjection,
+            mapInstance,
+          });
+        }
+        return undefined;
+      });
+    }
+    return [];
+  }, [mapInstance, pointToProjection, shapes.data, modelElements]);
+
+  const features = useMemo(() => {
+    return elements
+      .filter((element): element is CustomMultiPolygon => element !== undefined)
+      .map(element => element.multiPolygonFeature);
+  }, [elements]);
+
+  const vectorSource = useMemo(() => {
+    return new VectorSource({
+      features,
+    });
+  }, [features]);
+
+  return useMemo(
+    () =>
+      new VectorLayer({
+        source: vectorSource,
+      }),
+    [vectorSource],
+  );
+};
diff --git a/src/components/Map/MapVectorViewer/utils/config/useOlMapLayers.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/config/useOlMapVectorLayers.test.ts
similarity index 67%
rename from src/components/Map/MapVectorViewer/utils/config/useOlMapLayers.test.ts
rename to src/components/Map/MapViewer/MapViewerVector/utils/config/useOlMapVectorLayers.test.ts
index 0dd9389dc3961bc7db5bb2a30f69034cf73da59a..eee20645689fe08ed27adb4803c8e567adce9e73 100644
--- a/src/components/Map/MapVectorViewer/utils/config/useOlMapLayers.test.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/config/useOlMapVectorLayers.test.ts
@@ -1,15 +1,12 @@
 /* eslint-disable no-magic-numbers */
-import { BACKGROUND_INITIAL_STATE_MOCK } from '@/redux/backgrounds/background.mock';
 import { MAP_DATA_INITIAL_STATE, OPENED_MAPS_INITIAL_STATE } from '@/redux/map/map.constants';
-import { initialMapStateFixture } from '@/redux/map/map.fixtures';
 import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore';
-import { renderHook, waitFor } from '@testing-library/react';
-import { Map } from 'ol';
+import { renderHook } from '@testing-library/react';
 import BaseLayer from 'ol/layer/Base';
 import VectorLayer from 'ol/layer/Vector';
 import React from 'react';
-import { useOlMapLayers } from '@/components/Map/MapVectorViewer/utils/config/useOlMapLayers';
-import { useOlMap } from '@/components/Map/MapVectorViewer/utils/useOlMap';
+import { useOlMap } from '@/components/Map/MapViewer/utils/useOlMap';
+import { useOlMapVectorLayers } from '@/components/Map/MapViewer/MapViewerVector/utils/config/useOlMapVectorLayers';
 
 const useRefValue = {
   current: null,
@@ -33,24 +30,6 @@ Object.defineProperty(useRefValue, 'current', {
 jest.spyOn(React, 'useRef').mockReturnValue(useRefValue);
 
 describe('useOlMapLayers - util', () => {
-  it('should modify layers of the map instance on init', async () => {
-    const { Wrapper } = getReduxWrapperWithStore({
-      map: initialMapStateFixture,
-      backgrounds: BACKGROUND_INITIAL_STATE_MOCK,
-    });
-
-    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));
-  });
-
   const getRenderedHookResults = (): BaseLayer[] => {
     const { Wrapper } = getReduxWrapperWithStore({
       map: {
@@ -88,7 +67,7 @@ describe('useOlMapLayers - util', () => {
     });
 
     const { result } = renderHook(
-      () => useOlMapLayers({ mapInstance: hohResult.current.mapInstance }),
+      () => useOlMapVectorLayers({ mapInstance: hohResult.current.mapInstance }),
       {
         wrapper: Wrapper,
       },
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/config/useOlMapVectorLayers.ts b/src/components/Map/MapViewer/MapViewerVector/utils/config/useOlMapVectorLayers.ts
new file mode 100644
index 0000000000000000000000000000000000000000..52cf8ab0c4b70dad32e1733178ba511edd77ec9e
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/config/useOlMapVectorLayers.ts
@@ -0,0 +1,16 @@
+/* eslint-disable no-magic-numbers */
+import { MapInstance } from '@/types/map';
+import { useOlMapWhiteCardLayer } from '@/components/Map/MapViewer/MapViewerVector/utils/config/useOlMapWhiteCardLayer';
+import { MapConfig } from '../../MapViewerVector.types';
+import { useOlMapReactionsLayer } from './reactionsLayer/useOlMapReactionsLayer';
+
+interface UseOlMapLayersInput {
+  mapInstance: MapInstance;
+}
+
+export const useOlMapVectorLayers = ({ mapInstance }: UseOlMapLayersInput): MapConfig['layers'] => {
+  const reactionsLayer = useOlMapReactionsLayer({ mapInstance });
+  const whiteCardLayer = useOlMapWhiteCardLayer();
+
+  return [whiteCardLayer, reactionsLayer];
+};
diff --git a/src/components/Map/MapVectorViewer/utils/config/useOlMapWhiteCardLayer.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/config/useOlMapWhiteCardLayer.test.ts
similarity index 100%
rename from src/components/Map/MapVectorViewer/utils/config/useOlMapWhiteCardLayer.test.ts
rename to src/components/Map/MapViewer/MapViewerVector/utils/config/useOlMapWhiteCardLayer.test.ts
diff --git a/src/components/Map/MapVectorViewer/utils/config/useOlMapWhiteCardLayer.ts b/src/components/Map/MapViewer/MapViewerVector/utils/config/useOlMapWhiteCardLayer.ts
similarity index 100%
rename from src/components/Map/MapVectorViewer/utils/config/useOlMapWhiteCardLayer.ts
rename to src/components/Map/MapViewer/MapViewerVector/utils/config/useOlMapWhiteCardLayer.ts
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/CustomMultiPolygon.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/CustomMultiPolygon.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f5de8dbe0c3516fbd031b857f3a654c09585efbc
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/CustomMultiPolygon.test.ts
@@ -0,0 +1,120 @@
+/* eslint-disable no-magic-numbers */
+import { Feature, Map } from 'ol';
+import { Fill, Style, Text } from 'ol/style';
+import { Polygon, MultiPolygon } from 'ol/geom';
+import getText from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/getText';
+import getMultiPolygon from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/getMultiPolygon';
+import getStroke from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/getStroke';
+import getFill from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/getFill';
+import { rgbToHex } from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/rgbToHex';
+import CustomMultiPolygon, {
+  CustomMultiPolygonProps,
+} from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/CustomMultiPolygon';
+import View from 'ol/View';
+import {
+  WHITE_COLOR,
+  BLACK_COLOR,
+} from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants';
+
+jest.mock('./getText');
+jest.mock('./getMultiPolygon');
+jest.mock('./getStroke');
+jest.mock('./getFill');
+jest.mock('./rgbToHex');
+
+describe('CustomMultiPolygon', () => {
+  let props: CustomMultiPolygonProps;
+
+  beforeEach(() => {
+    const dummyElement = document.createElement('div');
+    const mapInstance = new Map({
+      target: dummyElement,
+      view: new View({
+        zoom: 5,
+        minZoom: 3,
+        maxZoom: 7,
+      }),
+    });
+    props = {
+      shapes: [],
+      x: 0,
+      y: 0,
+      width: 100,
+      height: 100,
+      zIndex: 1,
+      fillColor: WHITE_COLOR,
+      borderColor: BLACK_COLOR,
+      fontColor: BLACK_COLOR,
+      lineWidth: 2,
+      text: 'Test Text',
+      fontSize: 12,
+      nameX: 10,
+      nameY: 20,
+      nameHeight: 30,
+      nameWidth: 40,
+      nameVerticalAlign: 'MIDDLE',
+      nameHorizontalAlign: 'CENTER',
+      pointToProjection: jest.fn(),
+      mapInstance,
+    };
+
+    (getText as jest.Mock).mockReturnValue({
+      textCoords: [0, 0],
+      textStyle: new Style({
+        text: new Text({
+          text: props.text,
+          font: `bold ${props.fontSize}px Arial`,
+          fill: new Fill({
+            color: '#000',
+          }),
+          placement: 'point',
+          textAlign: 'center',
+          textBaseline: 'middle',
+        }),
+      }),
+    });
+    (getMultiPolygon as jest.Mock).mockReturnValue([
+      new Polygon([
+        [
+          [0, 0],
+          [1, 1],
+          [2, 2],
+        ],
+      ]),
+    ]);
+    (getStroke as jest.Mock).mockReturnValue(new Style());
+    (getFill as jest.Mock).mockReturnValue(new Style());
+    (rgbToHex as jest.Mock).mockReturnValue('#FFFFFF');
+  });
+
+  it('should initialize with correct default properties', () => {
+    const multiPolygon = new CustomMultiPolygon(props);
+
+    expect(multiPolygon.polygons.length).toBe(2);
+    expect(multiPolygon.multiPolygonFeature).toBeInstanceOf(Feature);
+    expect(multiPolygon.multiPolygonFeature.getGeometry()).toBeInstanceOf(MultiPolygon);
+  });
+
+  it('should apply correct styles to the feature', () => {
+    const multiPolygon = new CustomMultiPolygon(props);
+    const feature = multiPolygon.multiPolygonFeature;
+
+    const style = feature.getStyleFunction()?.call(multiPolygon, feature, 1);
+
+    if (Array.isArray(style)) {
+      expect(style.length).toBeGreaterThan(0);
+    } else {
+      expect(style).toBeInstanceOf(Style);
+    }
+  });
+
+  it('should update text style based on resolution', () => {
+    const multiPolygon = new CustomMultiPolygon(props);
+    const feature = multiPolygon.multiPolygonFeature;
+
+    multiPolygon.styleFunction(feature, 1000);
+    if (multiPolygon.textStyle) {
+      expect(multiPolygon.textStyle.getText()?.getScale()).toBe(1.22);
+    }
+  });
+});
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/CustomMultiPolygon.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/CustomMultiPolygon.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4b339096ca4ee28686df66a5e3edd930a60878d0
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/CustomMultiPolygon.ts
@@ -0,0 +1,150 @@
+/* eslint-disable no-magic-numbers */
+import { Style } from 'ol/style';
+import Feature, { FeatureLike } from 'ol/Feature';
+import { Geometry, MultiPolygon } from 'ol/geom';
+import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
+import getStroke from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/getStroke';
+import getFill from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/getFill';
+import RenderFeature from 'ol/render/Feature';
+import Polygon from 'ol/geom/Polygon';
+import getMultiPolygon from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/getMultiPolygon';
+import getText from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/getText';
+import { rgbToHex } from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/rgbToHex';
+import { Shape } from '@/types/models';
+import { MapInstance } from '@/types/map';
+import {
+  ColorObject,
+  HorizontalAlign,
+  VerticalAlign,
+} from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.types';
+import {
+  BLACK_COLOR,
+  WHITE_COLOR,
+} from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants';
+
+export type CustomMultiPolygonProps = {
+  shapes: Array<Shape>;
+  x: number;
+  y: number;
+  width: number;
+  height: number;
+  zIndex: number;
+  fillColor?: ColorObject;
+  borderColor?: ColorObject;
+  fontColor?: ColorObject;
+  lineWidth?: number;
+  text?: string;
+  fontSize?: string | number;
+  nameX: number;
+  nameY: number;
+  nameHeight: number;
+  nameWidth: number;
+  nameVerticalAlign?: VerticalAlign;
+  nameHorizontalAlign?: HorizontalAlign;
+  pointToProjection: UsePointToProjectionResult;
+  mapInstance: MapInstance;
+};
+
+export default class CustomMultiPolygon {
+  multiPolygonStyle: Style;
+
+  textStyle: Style | undefined;
+
+  polygons: Array<Polygon> = [];
+
+  multiPolygonFeature: Feature;
+
+  constructor({
+    shapes,
+    x,
+    y,
+    width,
+    height,
+    zIndex,
+    fillColor = WHITE_COLOR,
+    borderColor = BLACK_COLOR,
+    fontColor = BLACK_COLOR,
+    lineWidth = 1,
+    text = '',
+    fontSize = 12,
+    nameX,
+    nameY,
+    nameHeight,
+    nameWidth,
+    nameVerticalAlign = 'MIDDLE',
+    nameHorizontalAlign = 'CENTER',
+    pointToProjection,
+    mapInstance,
+  }: CustomMultiPolygonProps) {
+    if (text) {
+      const { textCoords, textStyle } = getText({
+        text,
+        fontSize,
+        x: nameX,
+        y: nameY,
+        width: nameWidth,
+        height: nameHeight,
+        color: rgbToHex(fontColor),
+        verticalAlign: nameVerticalAlign,
+        horizontalAlign: nameHorizontalAlign,
+        pointToProjection,
+      });
+      this.textStyle = new Style({
+        geometry: (feature: FeatureLike): Geometry | RenderFeature | undefined => {
+          const geometry = feature.getGeometry();
+          if (geometry && geometry.getType() === 'MultiPolygon') {
+            return (geometry as MultiPolygon).getPolygon(0).getInteriorPoint();
+          }
+          return undefined;
+        },
+        text: textStyle.getText() || undefined,
+        zIndex,
+      });
+      this.polygons.push(new Polygon([[textCoords, textCoords]]));
+    }
+
+    this.polygons = [
+      ...this.polygons,
+      ...getMultiPolygon({ x, y, width, height, shapes, pointToProjection }),
+    ];
+
+    this.multiPolygonFeature = new Feature({
+      geometry: new MultiPolygon([...this.polygons]),
+      getTextScale: (resolution: number): number => {
+        const maxZoom = mapInstance?.getView().getMaxZoom();
+        if (maxZoom) {
+          const minResolution = mapInstance?.getView().getResolutionForZoom(maxZoom);
+          if (minResolution) {
+            return Math.round((minResolution / resolution) * 100) / 100;
+          }
+        }
+        return 1;
+      },
+    });
+
+    this.multiPolygonStyle = new Style({
+      stroke: getStroke({ color: rgbToHex(borderColor), width: lineWidth }),
+      fill: getFill({ color: rgbToHex(fillColor) }),
+      zIndex,
+    });
+
+    this.multiPolygonFeature.setStyle(this.styleFunction.bind(this));
+  }
+
+  styleFunction(feature: FeatureLike, resolution: number): Style | Array<Style> | void {
+    const getTextScale = feature.get('getTextScale');
+    let textScale = 1;
+    if (getTextScale instanceof Function) {
+      textScale = getTextScale(resolution);
+    }
+    const styles = [];
+    if (this.textStyle) {
+      const text = this.textStyle.getText();
+      if (text) {
+        this.textStyle.getText()?.setScale(textScale);
+        styles.push(this.textStyle);
+      }
+    }
+    return [...styles, this.multiPolygonStyle];
+  }
+}
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getBezierCurve.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getBezierCurve.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cfb6a22c586c24efeedb0dd4725e7deb1a77e697
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getBezierCurve.test.ts
@@ -0,0 +1,78 @@
+/* eslint-disable no-magic-numbers */
+import getBezierCurve from './getBezierCurve';
+
+describe('getBezierCurve', () => {
+  it('should return an array of coordinates representing a Bezier curve', () => {
+    const p0 = [0, 0];
+    const p1 = [10, 20];
+    const p2 = [20, 20];
+    const p3 = [30, 0];
+
+    const result = getBezierCurve({
+      p0,
+      p1,
+      p2,
+      p3,
+      numPoints: 50,
+    });
+
+    expect(result).toHaveLength(51);
+
+    expect(result[0]).toEqual(p0);
+    expect(result[result.length - 1]).toEqual(p3);
+
+    result.forEach(coord => {
+      expect(coord).toHaveLength(2);
+      expect(typeof coord[0]).toBe('number');
+      expect(typeof coord[1]).toBe('number');
+    });
+  });
+
+  it('should generate a different number of points based on numPoints', () => {
+    const p0 = [0, 0];
+    const p1 = [10, 20];
+    const p2 = [20, 20];
+    const p3 = [30, 0];
+
+    const result100 = getBezierCurve({
+      p0,
+      p1,
+      p2,
+      p3,
+      numPoints: 100,
+    });
+
+    const result10 = getBezierCurve({
+      p0,
+      p1,
+      p2,
+      p3,
+      numPoints: 10,
+    });
+
+    expect(result100).toHaveLength(101);
+    expect(result10).toHaveLength(11);
+  });
+
+  it('should return correct coordinates for a simple straight line', () => {
+    const p0 = [0, 0];
+    const p1 = [0, 0];
+    const p2 = [10, 10];
+    const p3 = [10, 10];
+
+    const result = getBezierCurve({
+      p0,
+      p1,
+      p2,
+      p3,
+      numPoints: 10,
+    });
+
+    expect(result[0]).toEqual([0, 0]);
+    expect(result[result.length - 1]).toEqual([10, 10]);
+
+    result.forEach(coord => {
+      expect(coord[0]).toBe(coord[1]);
+    });
+  });
+});
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getBezierCurve.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getBezierCurve.ts
new file mode 100644
index 0000000000000000000000000000000000000000..814e7aac4dcab68cf9cb5a0e04b0763eaf97f0bd
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getBezierCurve.ts
@@ -0,0 +1,39 @@
+/* eslint-disable no-magic-numbers */
+import { Coordinate } from 'ol/coordinate';
+
+export default function getBezierCurve({
+  p0,
+  p1,
+  p2,
+  p3,
+  numPoints = 50,
+}: {
+  p0: Coordinate;
+  p1: Coordinate;
+  p2: Coordinate;
+  p3: Coordinate;
+  numPoints?: number;
+}): Array<Coordinate> {
+  const curve: Array<Coordinate> = [];
+
+  for (let i = 0; i < numPoints; i += 1) {
+    const t = parseFloat((i / (numPoints - 1)).toFixed(3));
+
+    const x =
+      (1 - t) ** 3 * p0[0] +
+      3 * (1 - t) ** 2 * t * p1[0] +
+      3 * (1 - t) * t ** 2 * p2[0] +
+      t ** 3 * p3[0];
+
+    const y =
+      (1 - t) ** 3 * p0[1] +
+      3 * (1 - t) ** 2 * t * p1[1] +
+      3 * (1 - t) * t ** 2 * p2[1] +
+      t ** 3 * p3[1];
+
+    curve.push([x, y]);
+  }
+  curve.push(p3);
+
+  return curve;
+}
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCoordsX.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCoordsX.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ddf8648e49fb6ade7eed11b17217fa3dd397385d
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCoordsX.test.ts
@@ -0,0 +1,53 @@
+/* eslint-disable no-magic-numbers */
+
+import getCoordsX from './getCoordsX';
+
+describe('getCoordsX', () => {
+  it('should calculate the correct coordinate when all parameters are provided', () => {
+    const x = 10;
+    const absoluteX = 20;
+    const relativeX = 10;
+    const relativeHeightForX = 5;
+    const height = 100;
+    const width = 200;
+
+    const result = getCoordsX(x, absoluteX, relativeX, relativeHeightForX, height, width);
+    expect(result).toBe(55);
+  });
+
+  it('should handle null for relativeHeightForX correctly', () => {
+    const x = 10;
+    const absoluteX = 20;
+    const relativeX = 10;
+    const relativeHeightForX = null;
+    const height = 100;
+    const width = 200;
+
+    const result = getCoordsX(x, absoluteX, relativeX, relativeHeightForX, height, width);
+    expect(result).toBe(50);
+  });
+
+  it('should calculate the correct coordinate when relativeX and relativeHeightForX are zero', () => {
+    const x = 10;
+    const absoluteX = 20;
+    const relativeX = 0;
+    const relativeHeightForX = 0;
+    const height = 100;
+    const width = 200;
+
+    const result = getCoordsX(x, absoluteX, relativeX, relativeHeightForX, height, width);
+    expect(result).toBe(30);
+  });
+
+  it('should handle negative values for parameters', () => {
+    const x = -10;
+    const absoluteX = -20;
+    const relativeX = -10;
+    const relativeHeightForX = -5;
+    const height = 100;
+    const width = 200;
+
+    const result = getCoordsX(x, absoluteX, relativeX, relativeHeightForX, height, width);
+    expect(result).toBe(-55);
+  });
+});
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCoordsX.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCoordsX.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4cc48ddbfb89cf1b12b2040998b0cb5a31dccf8b
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCoordsX.ts
@@ -0,0 +1,11 @@
+/* eslint-disable no-magic-numbers */
+export default function getCoordsX(
+  x: number,
+  absoluteX: number,
+  relativeX: number,
+  relativeHeightForX: number | null,
+  height: number,
+  width: number,
+): number {
+  return x + absoluteX + (width * relativeX) / 100 + (height * (relativeHeightForX || 0)) / 100;
+}
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCoordsY.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCoordsY.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d5c34139d43433c15c55b3878cc843b3f8517d08
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCoordsY.test.ts
@@ -0,0 +1,52 @@
+/* eslint-disable no-magic-numbers */
+import getCoordsY from './getCoordsY';
+
+describe('getCoordsY', () => {
+  it('should calculate the correct coordinate when all parameters are provided', () => {
+    const x = 10;
+    const absoluteY = 20;
+    const relativeY = 10;
+    const relativeWidthForY = 5;
+    const height = 80;
+    const width = 150;
+
+    const result = getCoordsY(x, absoluteY, relativeY, relativeWidthForY, height, width);
+    expect(result).toBe(45.5);
+  });
+
+  it('should handle null for relativeWidthForY correctly', () => {
+    const x = 10;
+    const absoluteY = 20;
+    const relativeY = 10;
+    const relativeWidthForY = null;
+    const height = 100;
+    const width = 140;
+
+    const result = getCoordsY(x, absoluteY, relativeY, relativeWidthForY, height, width);
+    expect(result).toBe(40);
+  });
+
+  it('should calculate the correct coordinate when relativeY and relativeWidthForY are zero', () => {
+    const x = 10;
+    const absoluteY = 20;
+    const relativeY = 0;
+    const relativeWidthForY = 0;
+    const height = 100;
+    const width = 200;
+
+    const result = getCoordsY(x, absoluteY, relativeY, relativeWidthForY, height, width);
+    expect(result).toBe(30);
+  });
+
+  it('should handle negative values for parameters', () => {
+    const x = -10;
+    const absoluteY = -20;
+    const relativeY = -10;
+    const relativeWidthForY = -5;
+    const height = 100;
+    const width = 200;
+
+    const result = getCoordsY(x, absoluteY, relativeY, relativeWidthForY, height, width);
+    expect(result).toBe(-50);
+  });
+});
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCoordsY.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCoordsY.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2fc132f219bf75fd736f03ceddafdc767b39103e
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCoordsY.ts
@@ -0,0 +1,11 @@
+/* eslint-disable no-magic-numbers */
+export default function getCoordsY(
+  y: number,
+  absoluteY: number,
+  relativeY: number,
+  relativeWidthForY: number | null,
+  height: number,
+  width: number,
+): number {
+  return y + absoluteY + (height * relativeY) / 100 + (width * (relativeWidthForY || 0)) / 100;
+}
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCurveCoords.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCurveCoords.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8ce8b569bb7a63899c8e4cea5895ae78d73b775d
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCurveCoords.test.ts
@@ -0,0 +1,72 @@
+/* eslint-disable no-magic-numbers */
+import getCoordsX from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/getCoordsX';
+import getCoordsY from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/getCoordsY';
+import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
+import { ShapeCurvePoint } from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.types';
+import getCurveCoords from './getCurveCoords';
+
+jest.mock('./getCoordsX');
+jest.mock('./getCoordsY');
+
+describe('getCurveCoords', () => {
+  const mockPointToProjection: UsePointToProjectionResult = jest.fn(point => [point.x, point.y]);
+
+  beforeEach(() => {
+    jest.clearAllMocks();
+  });
+
+  it('should calculate correct coordinates for p1, p2, and p3', () => {
+    const x = 10;
+    const y = 20;
+    const height = 100;
+    const width = 200;
+
+    const point: ShapeCurvePoint = {
+      type: 'REL_ABS_POINT',
+      absoluteX1: 10,
+      relativeX1: 5,
+      relativeHeightForX1: 2,
+      absoluteY1: 15,
+      relativeY1: 3,
+      relativeWidthForY1: 1,
+      absoluteX2: 12,
+      relativeX2: 7,
+      relativeHeightForX2: 4,
+      absoluteY2: 16,
+      relativeY2: 4,
+      relativeWidthForY2: 2,
+      absoluteX3: 14,
+      relativeX3: 9,
+      relativeHeightForX3: 6,
+      absoluteY3: 18,
+      relativeY3: 5,
+      relativeWidthForY3: 3,
+    };
+
+    (getCoordsX as jest.Mock)
+      .mockReturnValueOnce(25)
+      .mockReturnValueOnce(35)
+      .mockReturnValueOnce(45);
+    (getCoordsY as jest.Mock)
+      .mockReturnValueOnce(30)
+      .mockReturnValueOnce(40)
+      .mockReturnValueOnce(50);
+
+    const result = getCurveCoords({
+      x,
+      y,
+      point,
+      height,
+      width,
+      pointToProjection: mockPointToProjection,
+    });
+
+    expect(result.p1).toEqual([35, 40]);
+    expect(result.p2).toEqual([45, 50]);
+    expect(result.p3).toEqual([25, 30]);
+
+    expect(getCoordsX).toHaveBeenCalledTimes(3);
+    expect(getCoordsY).toHaveBeenCalledTimes(3);
+    expect(mockPointToProjection).toHaveBeenCalledTimes(3);
+  });
+});
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCurveCoords.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCurveCoords.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2b6956348eafea711c9636ced732b4fa3422d729
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCurveCoords.ts
@@ -0,0 +1,35 @@
+import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
+import { Coordinate } from 'ol/coordinate';
+import getCoordsX from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/getCoordsX';
+import getCoordsY from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/getCoordsY';
+import { ShapeCurvePoint } from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.types';
+
+export default function getCurveCoords({
+  x,
+  y,
+  point,
+  height,
+  width,
+  pointToProjection,
+}: {
+  x: number;
+  y: number;
+  point: ShapeCurvePoint;
+  height: number;
+  width: number;
+  pointToProjection: UsePointToProjectionResult;
+}): { p1: Coordinate; p2: Coordinate; p3: Coordinate } {
+  const p3 = pointToProjection({
+    x: getCoordsX(x, point.absoluteX1, point.relativeX1, point.relativeHeightForX1, height, width),
+    y: getCoordsY(y, point.absoluteY1, point.relativeY1, point.relativeWidthForY1, height, width),
+  });
+  const p1 = pointToProjection({
+    x: getCoordsX(x, point.absoluteX2, point.relativeX2, point.relativeHeightForX2, height, width),
+    y: getCoordsY(y, point.absoluteY2, point.relativeY2, point.relativeWidthForY2, height, width),
+  });
+  const p2 = pointToProjection({
+    x: getCoordsX(x, point.absoluteX3, point.relativeX3, point.relativeHeightForX3, height, width),
+    y: getCoordsY(y, point.absoluteY3, point.relativeY3, point.relativeWidthForY3, height, width),
+  });
+  return { p1, p2, p3 };
+}
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getEllipseCoords.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getEllipseCoords.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cf6f4c49f75ca81e8f569d17d040f7e55e6c2817
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getEllipseCoords.test.ts
@@ -0,0 +1,62 @@
+/* eslint-disable no-magic-numbers */
+import getCoordsX from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/getCoordsX';
+import getCoordsY from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/getCoordsY';
+import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
+import getEllipseCoords from './getEllipseCoords';
+
+jest.mock('./getCoordsX');
+jest.mock('./getCoordsY');
+
+describe('getEllipseCoords', () => {
+  const mockPointToProjection: UsePointToProjectionResult = jest.fn(point => [point.x, point.y]);
+
+  beforeEach(() => {
+    jest.clearAllMocks();
+  });
+
+  it('should calculate correct coordinates for an ellipse', () => {
+    const x = 10;
+    const y = 20;
+    const height = 100;
+    const width = 200;
+    const center = {
+      type: 'center',
+      absoluteX: 15,
+      absoluteY: 25,
+      relativeX: 5,
+      relativeY: 10,
+      relativeHeightForX: null,
+      relativeWidthForY: null,
+    };
+    const radius = {
+      type: 'radius',
+      absoluteX: 30,
+      absoluteY: 40,
+      relativeX: 10,
+      relativeY: 15,
+    };
+    const points = 4;
+
+    (getCoordsX as jest.Mock).mockReturnValue(35);
+    (getCoordsY as jest.Mock).mockReturnValue(55);
+
+    const result = getEllipseCoords({
+      x,
+      y,
+      center,
+      radius,
+      height,
+      width,
+      pointToProjection: mockPointToProjection,
+      points,
+    });
+
+    expect(result).toHaveLength(points + 1);
+
+    expect(result[0]).toEqual(result[result.length - 1]);
+
+    expect(getCoordsX).toHaveBeenCalledTimes(1);
+    expect(getCoordsY).toHaveBeenCalledTimes(1);
+    expect(mockPointToProjection).toHaveBeenCalledTimes(points);
+  });
+});
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getEllipseCoords.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getEllipseCoords.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fa713e8ab27865ac7c42a49cacb98f400e4123da
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getEllipseCoords.ts
@@ -0,0 +1,76 @@
+/* eslint-disable no-magic-numbers */
+import getCoordsX from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/getCoordsX';
+import getCoordsY from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/getCoordsY';
+import { Coordinate } from 'ol/coordinate';
+import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
+
+type EllipseCenter = {
+  type: string;
+  absoluteX: number;
+  absoluteY: number;
+  relativeX: number;
+  relativeY: number;
+  relativeHeightForX: number | null;
+  relativeWidthForY: number | null;
+};
+
+type EllipseRadius = {
+  type: string;
+  absoluteX: number;
+  absoluteY: number;
+  relativeX: number;
+  relativeY: number;
+};
+
+export default function getEllipseCoords({
+  x,
+  y,
+  center,
+  radius,
+  height,
+  width,
+  pointToProjection,
+  points = 64,
+}: {
+  x: number;
+  y: number;
+  center: EllipseCenter;
+  radius: EllipseRadius;
+  height: number;
+  width: number;
+  pointToProjection: UsePointToProjectionResult;
+  points?: number;
+}): Array<Coordinate> {
+  const centerX = getCoordsX(
+    x,
+    center.absoluteX,
+    center.relativeX,
+    center.relativeHeightForX,
+    height,
+    width,
+  );
+  const centerY = getCoordsY(
+    y,
+    center.absoluteY,
+    center.relativeY,
+    center.relativeWidthForY,
+    height,
+    width,
+  );
+  const radiusX = radius.absoluteX + (radius.relativeX * width) / 100;
+  const radiusY = radius.absoluteY + (radius.relativeY * height) / 100;
+  let angle;
+  let coordsX;
+  let coordsY;
+  const coordinates: Array<Coordinate> = [];
+
+  for (let i = 0; i < points; i += 1) {
+    angle = (i / points) * 2 * Math.PI;
+    coordsX = centerX + radiusX * Math.cos(angle);
+    coordsY = centerY + radiusY * Math.sin(angle);
+    coordinates.push(pointToProjection({ x: coordsX, y: coordsY }));
+  }
+  coordinates.push(coordinates[0]);
+
+  return coordinates;
+}
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getFill.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getFill.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2af3fe0fb197ee0851a9b51747aca5ba205b9d09
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getFill.test.ts
@@ -0,0 +1,17 @@
+import { Fill } from 'ol/style';
+import getFill from './getFill';
+
+describe('getFill', () => {
+  it('should return a Fill object with the default color when no color is provided', () => {
+    const result = getFill({});
+    expect(result).toBeInstanceOf(Fill);
+    expect(result.getColor()).toBe('#fff');
+  });
+
+  it('should return a Fill object with the provided color', () => {
+    const color = '#ff0000';
+    const result = getFill({ color });
+    expect(result).toBeInstanceOf(Fill);
+    expect(result.getColor()).toBe(color);
+  });
+});
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getFill.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getFill.ts
new file mode 100644
index 0000000000000000000000000000000000000000..78a401c2366654cfc207adee5258899dc626e982
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getFill.ts
@@ -0,0 +1,8 @@
+/* eslint-disable no-magic-numbers */
+import { Fill } from 'ol/style';
+
+export default function getFill({ color = '#fff' }: { color?: string }): Fill {
+  return new Fill({
+    color,
+  });
+}
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getMultiPolygon.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getMultiPolygon.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..76036f498b7d92ab4040ed570e0fd6b7959582e4
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getMultiPolygon.test.ts
@@ -0,0 +1,235 @@
+/* eslint-disable no-magic-numbers */
+import getPolygonCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/getPolygonCoords';
+import getEllipseCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/getEllipseCoords';
+import Polygon from 'ol/geom/Polygon';
+import { Shape } from '@/types/models';
+import getMultiPolygon from './getMultiPolygon';
+
+jest.mock('./getPolygonCoords');
+jest.mock('./getEllipseCoords');
+
+describe('getMultiPolygon', () => {
+  const mockPointToProjection = jest.fn(point => [point.x, point.y]);
+
+  beforeEach(() => {
+    jest.clearAllMocks();
+  });
+
+  it('should return an array of Polygons for POLYGON shapes', () => {
+    const x = 10;
+    const y = 20;
+    const width = 100;
+    const height = 200;
+
+    const shapes: Shape[] = [
+      {
+        type: 'POLYGON',
+        points: [
+          {
+            type: 'REL_ABS_POINT',
+            absoluteX: 0.0,
+            absoluteY: 0.0,
+            relativeX: 0.0,
+            relativeY: 0.0,
+            relativeHeightForX: null,
+            relativeWidthForY: null,
+          },
+          {
+            type: 'REL_ABS_POINT',
+            absoluteX: 0.0,
+            absoluteY: 0.0,
+            relativeX: 75.0,
+            relativeY: 0.0,
+            relativeHeightForX: null,
+            relativeWidthForY: null,
+          },
+          {
+            type: 'REL_ABS_POINT',
+            absoluteX: 0.0,
+            absoluteY: 0.0,
+            relativeX: 100.0,
+            relativeY: 100.0,
+            relativeHeightForX: null,
+            relativeWidthForY: null,
+          },
+          {
+            type: 'REL_ABS_POINT',
+            absoluteX: 0.0,
+            absoluteY: 0.0,
+            relativeX: 25.0,
+            relativeY: 100.0,
+            relativeHeightForX: null,
+            relativeWidthForY: null,
+          },
+        ],
+      },
+    ];
+
+    const mockPolygonCoords = [
+      [
+        [0, 0],
+        [2, 0],
+        [2, 2],
+        [0, 2],
+      ],
+    ];
+    (getPolygonCoords as jest.Mock).mockReturnValue(mockPolygonCoords);
+
+    const result = getMultiPolygon({
+      x,
+      y,
+      width,
+      height,
+      shapes,
+      pointToProjection: mockPointToProjection,
+    });
+
+    expect(result).toHaveLength(1);
+    expect(result[0]).toBeInstanceOf(Polygon);
+    expect(result[0].getCoordinates()).toEqual([mockPolygonCoords]);
+
+    expect(getPolygonCoords).toHaveBeenCalledTimes(1);
+  });
+
+  it('should return an array of Polygons for ELLIPSE shapes', () => {
+    const x = 30;
+    const y = 40;
+    const width = 100;
+    const height = 200;
+
+    const shapes: Shape[] = [
+      {
+        type: 'ELLIPSE',
+        center: {
+          type: 'REL_ABS_POINT',
+          absoluteX: 0.0,
+          absoluteY: 0.0,
+          relativeX: 50.0,
+          relativeY: 50.0,
+          relativeHeightForX: null,
+          relativeWidthForY: null,
+        },
+        radius: {
+          type: 'REL_ABS_RADIUS',
+          absoluteX: -7.0,
+          absoluteY: -7.0,
+          relativeX: 50.0,
+          relativeY: 50.0,
+        },
+      },
+    ];
+
+    const mockEllipseCoords = [
+      [
+        [0, 0],
+        [30, 0],
+        [45, 60],
+        [30, 120],
+        [0, 120],
+        [-15, 60],
+      ],
+    ];
+    (getEllipseCoords as jest.Mock).mockReturnValue(mockEllipseCoords);
+
+    const result = getMultiPolygon({
+      x,
+      y,
+      width,
+      height,
+      shapes,
+      pointToProjection: mockPointToProjection,
+    });
+
+    expect(result).toHaveLength(1);
+    expect(result[0]).toBeInstanceOf(Polygon);
+    expect(result[0].getCoordinates()).toEqual([mockEllipseCoords]);
+
+    expect(getEllipseCoords).toHaveBeenCalledTimes(1);
+  });
+
+  it('should handle multiple shapes (POLYGON and ELLIPSE)', () => {
+    const x = 10;
+    const y = 20;
+    const width = 100;
+    const height = 200;
+
+    const shapes: Shape[] = [
+      {
+        type: 'ELLIPSE',
+        center: {
+          type: 'REL_ABS_POINT',
+          absoluteX: 0.0,
+          absoluteY: 0.0,
+          relativeX: 50.0,
+          relativeY: 50.0,
+          relativeHeightForX: null,
+          relativeWidthForY: null,
+        },
+        radius: {
+          type: 'REL_ABS_RADIUS',
+          absoluteX: -7.0,
+          absoluteY: -7.0,
+          relativeX: 50.0,
+          relativeY: 50.0,
+        },
+      },
+      {
+        type: 'POLYGON',
+        points: [
+          {
+            type: 'REL_ABS_POINT',
+            absoluteX: 7.0,
+            absoluteY: 0.0,
+            relativeX: 50.0,
+            relativeY: 0.0,
+            relativeHeightForX: null,
+            relativeWidthForY: null,
+          },
+          {
+            type: 'REL_ABS_POINT',
+            absoluteX: -7.0,
+            absoluteY: 0.0,
+            relativeX: 50.0,
+            relativeY: 100.0,
+            relativeHeightForX: null,
+            relativeWidthForY: null,
+          },
+        ],
+      },
+    ];
+
+    const mockPolygonCoords = [
+      [
+        [0, 0],
+        [2, 0],
+        [2, 2],
+        [0, 2],
+      ],
+    ];
+    const mockEllipseCoords = [
+      [
+        [0, 0],
+        [30, 0],
+        [45, 60],
+        [30, 120],
+        [0, 120],
+        [-15, 60],
+      ],
+    ];
+    (getPolygonCoords as jest.Mock).mockReturnValue(mockPolygonCoords);
+    (getEllipseCoords as jest.Mock).mockReturnValue(mockEllipseCoords);
+
+    const result = getMultiPolygon({
+      x,
+      y,
+      width,
+      height,
+      shapes,
+      pointToProjection: mockPointToProjection,
+    });
+
+    expect(result).toHaveLength(2);
+    expect(getPolygonCoords).toHaveBeenCalledTimes(1);
+    expect(getEllipseCoords).toHaveBeenCalledTimes(1);
+  });
+});
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getMultiPolygon.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getMultiPolygon.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c417891bfc5474b5b70e614bde42aadc8e5aee8c
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getMultiPolygon.ts
@@ -0,0 +1,40 @@
+import getPolygonCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/getPolygonCoords';
+import getEllipseCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/getEllipseCoords';
+import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
+import Polygon from 'ol/geom/Polygon';
+import { Coordinate } from 'ol/coordinate';
+import { Shape } from '@/types/models';
+
+export default function getMultiPolygon({
+  x,
+  y,
+  width,
+  height,
+  shapes,
+  pointToProjection,
+}: {
+  x: number;
+  y: number;
+  width: number;
+  height: number;
+  shapes: Array<Shape>;
+  pointToProjection: UsePointToProjectionResult;
+}): Array<Polygon> {
+  return shapes.map(shape => {
+    let coords: Array<Coordinate> = [];
+    if (shape.type === 'POLYGON') {
+      coords = getPolygonCoords({ points: shape.points, x, y, height, width, pointToProjection });
+    } else if (shape.type === 'ELLIPSE') {
+      coords = getEllipseCoords({
+        x,
+        y,
+        center: shape.center,
+        radius: shape.radius,
+        height,
+        width,
+        pointToProjection,
+      });
+    }
+    return new Polygon([coords]);
+  });
+}
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getPolygonCoords.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getPolygonCoords.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e1911f7bc6ca8a5e0360c8662d7f76a4be94cbd2
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getPolygonCoords.test.ts
@@ -0,0 +1,142 @@
+/* eslint-disable no-magic-numbers */
+import getCoordsX from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/getCoordsX';
+import getCoordsY from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/getCoordsY';
+import getCurveCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/getCurveCoords';
+import getBezierCurve from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/getBezierCurve';
+import {
+  ShapePoint,
+  ShapeCurvePoint,
+} from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.types';
+import getPolygonCoords from './getPolygonCoords';
+
+jest.mock('./getCoordsX');
+jest.mock('./getCoordsY');
+jest.mock('./getCurveCoords');
+jest.mock('./getBezierCurve');
+
+describe('getPolygonCoords', () => {
+  const mockPointToProjection = jest.fn(point => [point.x, point.y]);
+
+  beforeEach(() => {
+    jest.clearAllMocks();
+  });
+
+  it('should return coordinates for ShapePoint (simple point)', () => {
+    const x = 10;
+    const y = 20;
+    const height = 100;
+    const width = 200;
+
+    const points: Array<ShapePoint | ShapeCurvePoint> = [
+      {
+        type: 'REL_ABS_POINT',
+        absoluteX: 0,
+        absoluteY: 0,
+        relativeX: 0,
+        relativeY: 0,
+        relativeHeightForX: null,
+        relativeWidthForY: null,
+      },
+      {
+        type: 'REL_ABS_POINT',
+        absoluteX: 50,
+        absoluteY: 50,
+        relativeX: 10,
+        relativeY: 10,
+        relativeHeightForX: null,
+        relativeWidthForY: null,
+      },
+    ];
+
+    (getCoordsX as jest.Mock).mockReturnValueOnce(10).mockReturnValueOnce(80);
+    (getCoordsY as jest.Mock).mockReturnValueOnce(20).mockReturnValueOnce(80);
+
+    const result = getPolygonCoords({
+      points,
+      x,
+      y,
+      height,
+      width,
+      pointToProjection: mockPointToProjection,
+    });
+
+    expect(result).toEqual([
+      [10, 20],
+      [80, 80],
+    ]);
+
+    expect(getCoordsX).toHaveBeenCalledTimes(2);
+    expect(getCoordsY).toHaveBeenCalledTimes(2);
+  });
+
+  it('should handle a mix of ShapePoint and ShapeCurvePoint', () => {
+    const x = 10;
+    const y = 20;
+    const height = 100;
+    const width = 200;
+
+    const points: Array<ShapePoint | ShapeCurvePoint> = [
+      {
+        type: 'REL_ABS_POINT',
+        absoluteX: 0,
+        absoluteY: 0,
+        relativeX: 0,
+        relativeY: 0,
+        relativeHeightForX: null,
+        relativeWidthForY: null,
+      },
+      {
+        type: 'REL_ABS_POINT',
+        absoluteX1: 50,
+        absoluteY1: 50,
+        relativeX1: 10,
+        relativeY1: 10,
+        relativeHeightForX1: null,
+        relativeWidthForY1: null,
+        absoluteX2: 60,
+        absoluteY2: 60,
+        relativeX2: 20,
+        relativeY2: 20,
+        relativeHeightForX2: null,
+        relativeWidthForY2: null,
+        absoluteX3: 70,
+        absoluteY3: 70,
+        relativeX3: 30,
+        relativeY3: 30,
+        relativeHeightForX3: null,
+        relativeWidthForY3: null,
+      },
+    ];
+
+    const mockBezierCurve = [
+      [15, 25],
+      [35, 45],
+      [55, 65],
+    ];
+
+    (getCoordsX as jest.Mock).mockReturnValueOnce(10);
+    (getCoordsY as jest.Mock).mockReturnValueOnce(20);
+    (getCurveCoords as jest.Mock).mockReturnValue({
+      p1: [30, 40],
+      p2: [50, 60],
+      p3: [70, 80],
+    });
+    (getBezierCurve as jest.Mock).mockReturnValue(mockBezierCurve);
+
+    const result = getPolygonCoords({
+      points,
+      x,
+      y,
+      height,
+      width,
+      pointToProjection: mockPointToProjection,
+    });
+
+    expect(result).toEqual([[10, 20], ...mockBezierCurve]);
+
+    expect(getCoordsX).toHaveBeenCalledTimes(1);
+    expect(getCoordsY).toHaveBeenCalledTimes(1);
+    expect(getCurveCoords).toHaveBeenCalledTimes(1);
+    expect(getBezierCurve).toHaveBeenCalledTimes(1);
+  });
+});
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getPolygonCoords.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getPolygonCoords.ts
new file mode 100644
index 0000000000000000000000000000000000000000..01f8169f07d14ac8ffe228a0110d35c9036927ed
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getPolygonCoords.ts
@@ -0,0 +1,53 @@
+/* eslint-disable no-magic-numbers */
+import { Coordinate } from 'ol/coordinate';
+import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
+import getBezierCurve from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/getBezierCurve';
+import getCoordsX from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/getCoordsX';
+import getCoordsY from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/getCoordsY';
+import getCurveCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/getCurveCoords';
+import {
+  ShapeCurvePoint,
+  ShapePoint,
+} from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.types';
+
+export default function getPolygonCoords({
+  points,
+  x,
+  y,
+  height,
+  width,
+  pointToProjection,
+}: {
+  points: Array<ShapePoint | ShapeCurvePoint>;
+  x: number;
+  y: number;
+  height: number;
+  width: number;
+  pointToProjection: UsePointToProjectionResult;
+}): Array<Coordinate> {
+  let lastPoint: Coordinate;
+  return points.flatMap(point => {
+    if ('absoluteX' in point) {
+      const coordsX = getCoordsX(
+        x,
+        point.absoluteX,
+        point.relativeX,
+        point.relativeHeightForX,
+        height,
+        width,
+      );
+      const coordsY = getCoordsY(
+        y,
+        point.absoluteY,
+        point.relativeY,
+        point.relativeWidthForY,
+        height,
+        width,
+      );
+      lastPoint = pointToProjection({ x: coordsX, y: coordsY });
+      return [[...lastPoint]];
+    }
+    const { p1, p2, p3 } = getCurveCoords({ x, y, point, height, width, pointToProjection });
+    return getBezierCurve({ p0: lastPoint, p1, p2, p3, numPoints: 50 });
+  });
+}
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getStroke.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getStroke.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..70a27921c1a7510ddf9f4792000176aa962b41d6
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getStroke.test.ts
@@ -0,0 +1,21 @@
+/* eslint-disable no-magic-numbers */
+import { Stroke } from 'ol/style';
+import getStroke from './getStroke';
+
+describe('getStroke', () => {
+  it('should return a Stoke object with the default color and width', () => {
+    const result = getStroke({});
+    expect(result).toBeInstanceOf(Stroke);
+    expect(result.getColor()).toBe('#000');
+    expect(result.getWidth()).toBe(1);
+  });
+
+  it('should return a Stoke object with the provided color and width', () => {
+    const color = '#ff0000';
+    const width = 2;
+    const result = getStroke({ color, width });
+    expect(result).toBeInstanceOf(Stroke);
+    expect(result.getColor()).toBe(color);
+    expect(result.getWidth()).toBe(width);
+  });
+});
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getStroke.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getStroke.ts
new file mode 100644
index 0000000000000000000000000000000000000000..37816224714777d7917bc77067caa4354b75c8ee
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getStroke.ts
@@ -0,0 +1,15 @@
+/* eslint-disable no-magic-numbers */
+import { Stroke } from 'ol/style';
+
+export default function getStroke({
+  color = '#000',
+  width = 1,
+}: {
+  color?: string;
+  width?: number;
+}): Stroke {
+  return new Stroke({
+    color,
+    width,
+  });
+}
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getText.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getText.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a9f0490707370aca9a3e9fe595aab4e83174bcff
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getText.test.ts
@@ -0,0 +1,41 @@
+/* eslint-disable no-magic-numbers */
+import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
+import getText from './getText';
+
+describe('getText', () => {
+  const mockPointToProjection: UsePointToProjectionResult = jest.fn(point => [point.x, point.y]);
+  it('should return correct text coordinates and style when text is centered', () => {
+    const { textCoords, textStyle } = getText({
+      x: 0,
+      y: 0,
+      height: 100,
+      width: 100,
+      text: 'Text test',
+      fontSize: 12,
+      verticalAlign: 'MIDDLE',
+      horizontalAlign: 'CENTER',
+      pointToProjection: mockPointToProjection,
+    });
+
+    expect(textCoords).toEqual([50, 50]);
+    expect(textStyle.getText()?.getFont()).toEqual('bold 12px Arial');
+  });
+
+  it('should return correct text coordinates and style when text is aligned to bottom', () => {
+    const { textCoords, textStyle } = getText({
+      x: 20,
+      y: 30,
+      height: 100,
+      width: 100,
+      text: 'Text test',
+      fontSize: 18,
+      verticalAlign: 'BOTTOM',
+      horizontalAlign: 'CENTER',
+      pointToProjection: mockPointToProjection,
+    });
+
+    expect(textCoords).toEqual([70, 121]);
+
+    expect(textStyle.getText()?.getFont()).toEqual('bold 18px Arial');
+  });
+});
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getText.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getText.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b9a1f867e70d7752afda69c075119bf544127cf6
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getText.ts
@@ -0,0 +1,73 @@
+/* eslint-disable no-magic-numbers */
+import { Fill, Text } from 'ol/style';
+import Style from 'ol/style/Style';
+import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
+import { Feature } from 'ol';
+import { Point } from 'ol/geom';
+import { Coordinate } from 'ol/coordinate';
+import {
+  HorizontalAlign,
+  VerticalAlign,
+} from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.types';
+
+export default function getText({
+  x,
+  y,
+  height,
+  width,
+  text = '',
+  fontSize = 12,
+  color = '#000',
+  verticalAlign = 'MIDDLE',
+  horizontalAlign = 'CENTER',
+  pointToProjection,
+}: {
+  x: number;
+  y: number;
+  height: number;
+  width: number;
+  text: string;
+  fontSize?: string | number;
+  color?: string;
+  verticalAlign?: VerticalAlign;
+  horizontalAlign?: HorizontalAlign;
+  pointToProjection: UsePointToProjectionResult;
+}): { textCoords: Coordinate; textStyle: Style } {
+  const minX = x;
+  const maxX = x + width;
+  const minY = y;
+  const maxY = y + height;
+
+  let textY = (minY + maxY) / 2;
+  if (verticalAlign === 'TOP') {
+    textY = minY + +fontSize / 2;
+  } else if (verticalAlign === 'BOTTOM') {
+    textY = maxY - +fontSize / 2;
+  }
+
+  let textX = (minX + maxX) / 2;
+  if (['LEFT', 'START'].includes(horizontalAlign)) {
+    textX = minX;
+  } else if (['RIGHT', 'END'].includes(horizontalAlign)) {
+    textX = maxX;
+  }
+
+  const textCoords = pointToProjection({ x: textX, y: textY });
+  const textFeature = new Feature({ geometry: new Point(textCoords) });
+
+  const textStyle = new Style({
+    text: new Text({
+      text,
+      font: `bold ${fontSize}px Arial`,
+      fill: new Fill({
+        color,
+      }),
+      placement: 'point',
+      textAlign: 'center',
+      textBaseline: 'middle',
+    }),
+  });
+  textFeature.setStyle(textStyle);
+
+  return { textCoords, textStyle };
+}
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/rgbToHex.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/rgbToHex.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7c92f413ac1c369b46681b30ca37fd40e0749260
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/rgbToHex.test.ts
@@ -0,0 +1,26 @@
+import { ColorObject } from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.types';
+import { rgbToHex } from './rgbToHex';
+
+describe('rgbToHex', () => {
+  it('should correctly convert RGB and alpha values to hex', () => {
+    const color: ColorObject = {
+      rgb: -16222216,
+      alpha: 255,
+    };
+
+    const result = rgbToHex(color);
+
+    expect(result).toBe('#0877F8FF');
+  });
+
+  it('should correctly handle alpha values less than 255', () => {
+    const color: ColorObject = {
+      rgb: -16777216,
+      alpha: 128,
+    };
+
+    const result = rgbToHex(color);
+
+    expect(result).toBe('#00000080');
+  });
+});
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/rgbToHex.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/rgbToHex.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1fe9df8418b92bcd0a942a13b23155d299c76fdc
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/rgbToHex.ts
@@ -0,0 +1,9 @@
+/* eslint-disable no-magic-numbers */
+import { ColorObject } from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.types';
+
+export function rgbToHex(color: ColorObject): string {
+  const positiveRgb = color.rgb < 0 ? color.rgb + 0x100000000 : color.rgb;
+  const hexRgb = positiveRgb.toString(16).slice(-6).padStart(6, '0').toUpperCase();
+  const hexAlpha = color.alpha.toString(16).padStart(2, '0').toUpperCase();
+  return `#${hexRgb}${hexAlpha}`;
+}
diff --git a/src/components/Map/MapViewer/utils/config/useOlMapLayers.test.ts b/src/components/Map/MapViewer/utils/config/useOlMapLayers.test.ts
index cee56323c59bb31a4071c527285eca44a37c9c44..c8d29fa33d44ff5b00be2eac73e8d57364d47b12 100644
--- a/src/components/Map/MapViewer/utils/config/useOlMapLayers.test.ts
+++ b/src/components/Map/MapViewer/utils/config/useOlMapLayers.test.ts
@@ -1,15 +1,11 @@
 /* eslint-disable no-magic-numbers */
-import { BACKGROUND_INITIAL_STATE_MOCK } from '@/redux/backgrounds/background.mock';
 import { MAP_DATA_INITIAL_STATE, OPENED_MAPS_INITIAL_STATE } from '@/redux/map/map.constants';
-import { initialMapStateFixture } from '@/redux/map/map.fixtures';
 import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore';
-import { renderHook, waitFor } from '@testing-library/react';
-import { Map } from 'ol';
+import { renderHook } from '@testing-library/react';
 import BaseLayer from 'ol/layer/Base';
 import TileLayer from 'ol/layer/Tile';
 import VectorLayer from 'ol/layer/Vector';
 import React from 'react';
-import { useOlMap } from '../useOlMap';
 import { useOlMapLayers } from './useOlMapLayers';
 
 const useRefValue = {
@@ -34,24 +30,6 @@ Object.defineProperty(useRefValue, 'current', {
 jest.spyOn(React, 'useRef').mockReturnValue(useRefValue);
 
 describe('useOlMapLayers - util', () => {
-  it('should modify layers of the map instance on init', async () => {
-    const { Wrapper } = getReduxWrapperWithStore({
-      map: initialMapStateFixture,
-      backgrounds: BACKGROUND_INITIAL_STATE_MOCK,
-    });
-
-    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));
-  });
-
   const getRenderedHookResults = (): BaseLayer[] => {
     const { Wrapper } = getReduxWrapperWithStore({
       map: {
@@ -83,18 +61,11 @@ describe('useOlMapLayers - util', () => {
         openedMaps: OPENED_MAPS_INITIAL_STATE,
       },
     });
-    const dummyElement = document.createElement('div');
-    const { result: hohResult } = renderHook(() => useOlMap({ target: dummyElement }), {
+
+    const { result } = renderHook(() => useOlMapLayers(), {
       wrapper: Wrapper,
     });
 
-    const { result } = renderHook(
-      () => useOlMapLayers({ mapInstance: hohResult.current.mapInstance }),
-      {
-        wrapper: Wrapper,
-      },
-    );
-
     return result.current;
   };
 
diff --git a/src/components/Map/MapViewer/utils/config/useOlMapLayers.ts b/src/components/Map/MapViewer/utils/config/useOlMapLayers.ts
index 5739c30201df49638f2e1656189c0ab561886450..925b92d842cafa41753999063738d69b13d886b0 100644
--- a/src/components/Map/MapViewer/utils/config/useOlMapLayers.ts
+++ b/src/components/Map/MapViewer/utils/config/useOlMapLayers.ts
@@ -1,6 +1,4 @@
 /* eslint-disable no-magic-numbers */
-import { MapInstance } from '@/types/map';
-import { useEffect } from 'react';
 import { useOlMapCommentsLayer } from '@/components/Map/MapViewer/utils/config/commentsLayer/useOlMapCommentsLayer';
 import { MapConfig } from '../../MapViewer.types';
 import { useOlMapOverlaysLayer } from './overlaysLayer/useOlMapOverlaysLayer';
@@ -8,24 +6,12 @@ import { useOlMapPinsLayer } from './pinsLayer/useOlMapPinsLayer';
 import { useOlMapReactionsLayer } from './reactionsLayer/useOlMapReactionsLayer';
 import { useOlMapTileLayer } from './useOlMapTileLayer';
 
-interface UseOlMapLayersInput {
-  mapInstance: MapInstance;
-}
-
-export const useOlMapLayers = ({ mapInstance }: UseOlMapLayersInput): MapConfig['layers'] => {
+export const useOlMapLayers = (): MapConfig['layers'] => {
   const tileLayer = useOlMapTileLayer();
   const pinsLayer = useOlMapPinsLayer();
   const reactionsLayer = useOlMapReactionsLayer();
   const overlaysLayer = useOlMapOverlaysLayer();
   const commentsLayer = useOlMapCommentsLayer();
 
-  useEffect(() => {
-    if (!mapInstance) {
-      return;
-    }
-
-    mapInstance.setLayers([tileLayer, reactionsLayer, overlaysLayer, pinsLayer, commentsLayer]);
-  }, [reactionsLayer, tileLayer, pinsLayer, mapInstance, overlaysLayer, commentsLayer]);
-
-  return [tileLayer, pinsLayer, reactionsLayer, overlaysLayer];
+  return [tileLayer, pinsLayer, reactionsLayer, overlaysLayer, commentsLayer];
 };
diff --git a/src/components/Map/MapViewer/utils/listeners/mapSingleClick/onMapSingleClick.ts b/src/components/Map/MapViewer/utils/listeners/mapSingleClick/onMapSingleClick.ts
index e04f2880a546ad753225e2301de34487753f3c53..bc86c2326c8b0d31c21e7d2f0839d9b1332f4cba 100644
--- a/src/components/Map/MapViewer/utils/listeners/mapSingleClick/onMapSingleClick.ts
+++ b/src/components/Map/MapViewer/utils/listeners/mapSingleClick/onMapSingleClick.ts
@@ -38,6 +38,5 @@ export const onMapSingleClick =
       if (!searchResults || searchResults.length === SIZE_OF_EMPTY_ARRAY) {
         return;
       }
-
       handleSearchResultAction({ searchResults, dispatch, point, searchDistance, maxZoom, zoom, isResultDrawerOpen });
     };
diff --git a/src/components/Map/MapViewer/utils/listeners/useOlMapListeners.ts b/src/components/Map/MapViewer/utils/listeners/useOlMapListeners.ts
index 33013f2182bcfc3a424a5568ec7be512966e4d4d..c0dbb4b334030bedc41334617286d886b64286d0 100644
--- a/src/components/Map/MapViewer/utils/listeners/useOlMapListeners.ts
+++ b/src/components/Map/MapViewer/utils/listeners/useOlMapListeners.ts
@@ -7,7 +7,7 @@ import {
   mapDataMaxZoomValue,
   mapDataSizeSelector,
 } from '@/redux/map/map.selectors';
-import { currentModelIdSelector } from '@/redux/models/models.selectors';
+import { currentModelIdSelector, vectorRenderingSelector } from '@/redux/models/models.selectors';
 import { MapInstance } from '@/types/map';
 import { View } from 'ol';
 import { unByKey } from 'ol/Observable';
@@ -17,6 +17,7 @@ import { useEffect, useRef } from 'react';
 import { useSelector } from 'react-redux';
 import { useDebouncedCallback } from 'use-debounce';
 import { allCommentsSelectorOfCurrentMap } from '@/redux/comment/comment.selectors';
+import { useAppSelector } from '@/redux/hooks/useAppSelector';
 import { onMapRightClick } from './mapRightClick/onMapRightClick';
 import { onMapSingleClick } from './mapSingleClick/onMapSingleClick';
 import { onMapPositionChange } from './onMapPositionChange';
@@ -40,7 +41,7 @@ export const useOlMapListeners = ({ view, mapInstance }: UseOlMapListenersInput)
   const dispatch = useAppDispatch();
 
   const comments = useSelector(allCommentsSelectorOfCurrentMap);
-
+  const vectorRendering = useAppSelector(vectorRenderingSelector);
   useHandlePinIconClick();
 
   const handleRightClick = useDebouncedCallback(
@@ -74,12 +75,11 @@ export const useOlMapListeners = ({ view, mapInstance }: UseOlMapListenersInput)
 
   useEffect(() => {
     const key = view.on('change:center', handleChangeCenter);
-
     return () => unByKey(key);
-  }, [view, handleChangeCenter]);
+  }, [view, handleChangeCenter, vectorRendering]);
 
   useEffect(() => {
-    if (!mapInstance) {
+    if (!mapInstance || vectorRendering) {
       return;
     }
 
@@ -87,10 +87,10 @@ export const useOlMapListeners = ({ view, mapInstance }: UseOlMapListenersInput)
 
     // eslint-disable-next-line consistent-return
     return () => unByKey(key);
-  }, [mapInstance]);
+  }, [mapInstance, vectorRendering]);
 
   useEffect(() => {
-    if (!mapInstance) {
+    if (!mapInstance || vectorRendering) {
       return;
     }
 
@@ -100,10 +100,10 @@ export const useOlMapListeners = ({ view, mapInstance }: UseOlMapListenersInput)
 
     // eslint-disable-next-line consistent-return
     return () => unByKey(key);
-  }, [mapInstance, handleMapSingleClick]);
+  }, [mapInstance, handleMapSingleClick, vectorRendering]);
 
   useEffect(() => {
-    if (!mapInstance) {
+    if (!mapInstance || vectorRendering) {
       return;
     }
 
@@ -120,5 +120,5 @@ export const useOlMapListeners = ({ view, mapInstance }: UseOlMapListenersInput)
 
     // eslint-disable-next-line consistent-return
     return () => mapInstance.getViewport().removeEventListener('contextmenu', rightClickEvent);
-  }, [mapInstance, handleRightClick]);
+  }, [mapInstance, handleRightClick, vectorRendering]);
 };
diff --git a/src/components/Map/MapViewer/utils/useOlMap.ts b/src/components/Map/MapViewer/utils/useOlMap.ts
index 49ec30027077460bfdde9b9ff2b6e89961c0273a..5244411aa621727be223cde1628d82c7d6817d7e 100644
--- a/src/components/Map/MapViewer/utils/useOlMap.ts
+++ b/src/components/Map/MapViewer/utils/useOlMap.ts
@@ -2,7 +2,11 @@ import { MapInstance } from '@/types/map';
 import { useMapInstance } from '@/utils/context/mapInstanceContext';
 import Map from 'ol/Map';
 import { Zoom } from 'ol/control';
-import React, { MutableRefObject, useEffect } from 'react';
+import React, { MutableRefObject, useEffect, useMemo } from 'react';
+import { useOlMapVectorLayers } from '@/components/Map/MapViewer/MapViewerVector/utils/config/useOlMapVectorLayers';
+import LayerGroup from 'ol/layer/Group';
+import { useAppSelector } from '@/redux/hooks/useAppSelector';
+import { vectorRenderingSelector } from '@/redux/models/models.selectors';
 import { useOlMapLayers } from './config/useOlMapLayers';
 import { useOlMapView } from './config/useOlMapView';
 import { useOlMapListeners } from './listeners/useOlMapListeners';
@@ -18,10 +22,23 @@ interface UseOlMapOutput {
 type UseOlMap = (input?: UseOlMapInput) => UseOlMapOutput;
 
 export const useOlMap: UseOlMap = ({ target } = {}) => {
+  const vectorRendering = useAppSelector(vectorRenderingSelector);
   const mapRef = React.useRef<null | HTMLDivElement>(null);
   const { mapInstance, handleSetMapInstance } = useMapInstance();
   const view = useOlMapView({ mapInstance });
-  useOlMapLayers({ mapInstance });
+
+  const rasterLayers = useOlMapLayers();
+  const rasterLayersGroup = useMemo(() => {
+    return new LayerGroup({
+      layers: rasterLayers,
+    });
+  }, [rasterLayers]);
+  const vectorLayers = useOlMapVectorLayers({ mapInstance });
+  const vectorLayersGroup = useMemo(() => {
+    return new LayerGroup({
+      layers: vectorLayers,
+    });
+  }, [vectorLayers]);
   useOlMapListeners({ view, mapInstance });
 
   useEffect(() => {
@@ -44,6 +61,23 @@ export const useOlMap: UseOlMap = ({ target } = {}) => {
     handleSetMapInstance(map);
   }, [target, handleSetMapInstance]);
 
+  useEffect(() => {
+    if (!mapInstance) {
+      return;
+    }
+    mapInstance.setLayers([vectorLayersGroup, rasterLayersGroup]);
+  }, [mapInstance, rasterLayersGroup, vectorLayersGroup]);
+
+  useEffect(() => {
+    if (vectorRendering) {
+      rasterLayersGroup.setVisible(false);
+      vectorLayersGroup.setVisible(true);
+    } else {
+      vectorLayersGroup.setVisible(false);
+      rasterLayersGroup.setVisible(true);
+    }
+  }, [rasterLayersGroup, vectorLayersGroup, vectorRendering]);
+
   return {
     mapRef,
     mapInstance,
diff --git a/src/models/bioShapeSchema.ts b/src/models/bioShapeSchema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..273cf028fb763bb327ada5b692b747d7f0027b36
--- /dev/null
+++ b/src/models/bioShapeSchema.ts
@@ -0,0 +1,7 @@
+import { z } from 'zod';
+import { shapeSchema } from '@/models/shapeSchema';
+
+export const bioShapeSchema = z.object({
+  sboTerm: z.string(),
+  shapes: z.array(shapeSchema),
+});
diff --git a/src/models/fixtures/modelElementsFixture.ts b/src/models/fixtures/modelElementsFixture.ts
new file mode 100644
index 0000000000000000000000000000000000000000..87c8fb2edbe3b7183b177da0cce8d1d302603c19
--- /dev/null
+++ b/src/models/fixtures/modelElementsFixture.ts
@@ -0,0 +1,9 @@
+import { ZOD_SEED } from '@/constants';
+// eslint-disable-next-line import/no-extraneous-dependencies
+import { createFixture } from 'zod-fixture';
+import { modelElementsSchema } from '@/models/modelElementsSchema';
+
+export const modelElementsFixture = createFixture(modelElementsSchema, {
+  seed: ZOD_SEED,
+  array: { min: 3, max: 3 },
+});
diff --git a/src/models/fixtures/shapesFixture.ts b/src/models/fixtures/shapesFixture.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e1ce1e00f3772e84fe94c31173c12aa6da986202
--- /dev/null
+++ b/src/models/fixtures/shapesFixture.ts
@@ -0,0 +1,10 @@
+import { ZOD_SEED } from '@/constants';
+import { z } from 'zod';
+// eslint-disable-next-line import/no-extraneous-dependencies
+import { createFixture } from 'zod-fixture';
+import { bioShapeSchema } from '@/models/bioShapeSchema';
+
+export const shapesFixture = createFixture(z.array(bioShapeSchema), {
+  seed: ZOD_SEED,
+  array: { min: 3, max: 3 },
+});
diff --git a/src/models/modelElementSchema.ts b/src/models/modelElementSchema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0555bd9ce7b8a7d48d1ba9f8760725c7400018e4
--- /dev/null
+++ b/src/models/modelElementSchema.ts
@@ -0,0 +1,50 @@
+import { z } from 'zod';
+import { colorSchema } from '@/models/colorSchema';
+import { referenceSchema } from '@/models/referenceSchema';
+import { submodelSchema } from '@/models/submodelSchema';
+
+export const modelElementSchema = z.object({
+  id: z.number(),
+  model: z.number().nullable(),
+  glyph: z.number().nullable(),
+  submodel: submodelSchema.nullable(),
+  x: z.number(),
+  y: z.number(),
+  z: z.number(),
+  width: z.number(),
+  height: z.number(),
+  fontSize: z.number(),
+  fontColor: colorSchema,
+  fillColor: colorSchema,
+  borderColor: colorSchema,
+  visibilityLevel: z.string(),
+  transparencyLevel: z.string(),
+  notes: z.string(),
+  symbol: z.string().nullable(),
+  fullName: z.string().nullable(),
+  abbreviation: z.string().nullable(),
+  formula: z.string().nullable(),
+  name: z.string(),
+  nameX: z.number(),
+  nameY: z.number(),
+  nameWidth: z.number(),
+  nameHeight: z.number(),
+  nameVerticalAlign: z.enum(['TOP', 'MIDDLE', 'BOTTOM']),
+  nameHorizontalAlign: z.enum(['LEFT', 'RIGHT', 'CENTER', 'END', 'START']),
+  synonyms: z.array(z.string()),
+  formerSymbols: z.array(z.string()),
+  activity: z.boolean().optional(),
+  lineWidth: z.number().optional(),
+  complex: z.number().nullable().optional(),
+  initialAmount: z.number().nullable().optional(),
+  charge: z.number().nullable().optional(),
+  initialConcentration: z.number().nullable().optional(),
+  onlySubstanceUnits: z.boolean().nullable().optional(),
+  homodimer: z.number().nullable().optional(),
+  hypothetical: z.boolean().nullable().optional(),
+  boundaryCondition: z.boolean().nullable().optional(),
+  constant: z.boolean().nullable().optional(),
+  substanceUnits: z.boolean().nullable().optional(),
+  references: z.array(referenceSchema),
+  sboTerm: z.string(),
+});
diff --git a/src/models/modelElementsSchema.ts b/src/models/modelElementsSchema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..19969ba1c00f2155a545e9c1a45ea2ce867a4eea
--- /dev/null
+++ b/src/models/modelElementsSchema.ts
@@ -0,0 +1,11 @@
+import { z } from 'zod';
+import { modelElementSchema } from '@/models/modelElementSchema';
+
+export const modelElementsSchema = z.object({
+  content: z.array(modelElementSchema),
+  totalPages: z.number(),
+  totalElements: z.number(),
+  numberOfElements: z.number(),
+  size: z.number(),
+  number: z.number(),
+});
diff --git a/src/models/shapeEllipseSchema.ts b/src/models/shapeEllipseSchema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..36059a6abde49caf9cba39518a9a6821ca9ec306
--- /dev/null
+++ b/src/models/shapeEllipseSchema.ts
@@ -0,0 +1,9 @@
+import { z } from 'zod';
+import { shapeRelAbsPointSchema } from '@/models/shapeRelAbsPointSchema';
+import { shapeRelAbsRadiusSchema } from '@/models/shapeRelAbsRadiusSchema';
+
+export const shapeEllipseSchema = z.object({
+  type: z.literal('ELLIPSE'),
+  center: shapeRelAbsPointSchema,
+  radius: shapeRelAbsRadiusSchema,
+});
diff --git a/src/models/shapePolygonSchema.ts b/src/models/shapePolygonSchema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ef39a4ee9319b9affb7deae4f7ade6767860fa15
--- /dev/null
+++ b/src/models/shapePolygonSchema.ts
@@ -0,0 +1,8 @@
+import { z } from 'zod';
+import { shapeRelAbsBezierPointSchema } from '@/models/shapeRelAbsBezierPointSchema';
+import { shapeRelAbsPointSchema } from '@/models/shapeRelAbsPointSchema';
+
+export const shapePolygonSchema = z.object({
+  type: z.literal('POLYGON'),
+  points: z.array(z.union([shapeRelAbsPointSchema, shapeRelAbsBezierPointSchema])),
+});
diff --git a/src/models/shapeRelAbsBezierPointSchema.ts b/src/models/shapeRelAbsBezierPointSchema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..94868549e88445ed241673a5b10bd2b6665a008c
--- /dev/null
+++ b/src/models/shapeRelAbsBezierPointSchema.ts
@@ -0,0 +1,23 @@
+import { z } from 'zod';
+
+export const shapeRelAbsBezierPointSchema = z.object({
+  type: z.literal('REL_ABS_BEZIER_POINT'),
+  absoluteX1: z.number(),
+  absoluteY1: z.number(),
+  relativeX1: z.number(),
+  relativeY1: z.number(),
+  relativeHeightForX1: z.number().nullable(),
+  relativeWidthForY1: z.number().nullable(),
+  absoluteX2: z.number(),
+  absoluteY2: z.number(),
+  relativeX2: z.number(),
+  relativeY2: z.number(),
+  relativeHeightForX2: z.number().nullable(),
+  relativeWidthForY2: z.number().nullable(),
+  absoluteX3: z.number(),
+  absoluteY3: z.number(),
+  relativeX3: z.number(),
+  relativeY3: z.number(),
+  relativeHeightForX3: z.number().nullable(),
+  relativeWidthForY3: z.number().nullable(),
+});
diff --git a/src/models/shapeRelAbsPointSchema.ts b/src/models/shapeRelAbsPointSchema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..26bc59a9361bf676be30ce70d18b4752b4837363
--- /dev/null
+++ b/src/models/shapeRelAbsPointSchema.ts
@@ -0,0 +1,11 @@
+import { z } from 'zod';
+
+export const shapeRelAbsPointSchema = z.object({
+  type: z.literal('REL_ABS_POINT'),
+  absoluteX: z.number(),
+  absoluteY: z.number(),
+  relativeX: z.number(),
+  relativeY: z.number(),
+  relativeHeightForX: z.number().nullable(),
+  relativeWidthForY: z.number().nullable(),
+});
diff --git a/src/models/shapeRelAbsRadiusSchema.ts b/src/models/shapeRelAbsRadiusSchema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6050859d0756ce6941822e74be36baa56e602f32
--- /dev/null
+++ b/src/models/shapeRelAbsRadiusSchema.ts
@@ -0,0 +1,9 @@
+import { z } from 'zod';
+
+export const shapeRelAbsRadiusSchema = z.object({
+  type: z.literal('REL_ABS_RADIUS'),
+  absoluteX: z.number(),
+  absoluteY: z.number(),
+  relativeX: z.number(),
+  relativeY: z.number(),
+});
diff --git a/src/models/shapeSchema.ts b/src/models/shapeSchema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ba86b238a60f9334802cf0d70c51dfe0501aa607
--- /dev/null
+++ b/src/models/shapeSchema.ts
@@ -0,0 +1,5 @@
+import { z } from 'zod';
+import { shapeEllipseSchema } from '@/models/shapeEllipseSchema';
+import { shapePolygonSchema } from '@/models/shapePolygonSchema';
+
+export const shapeSchema = z.union([shapeEllipseSchema, shapePolygonSchema]);
diff --git a/src/redux/apiPath.ts b/src/redux/apiPath.ts
index 944cfa00adaa176e99eb468559a8e841fac84832..01fae40c7ce0e33a8e140d734d054c440aa3425e 100644
--- a/src/redux/apiPath.ts
+++ b/src/redux/apiPath.ts
@@ -48,6 +48,9 @@ export const apiPath = {
   getChemicalsStringWithColumnsTarget: (columns: string, target: string): string =>
     `projects/${PROJECT_ID}/chemicals:search?columns=${columns}&target=${target}`,
   getModelsString: (): string => `projects/${PROJECT_ID}/models/`,
+  getModelElements: (modelId: number): string =>
+    `projects/${PROJECT_ID}/maps/${modelId}/bioEntities/elements/?size=10000`,
+  getShapes: (): string => `projects/${PROJECT_ID}/shapes/`,
   getChemicalsStringWithQuery: (searchQuery: string): string =>
     `projects/${PROJECT_ID}/chemicals:search?query=${searchQuery}`,
   getAllOverlaysByProjectIdQuery: (
diff --git a/src/redux/modelElements/modelElements.constants.ts b/src/redux/modelElements/modelElements.constants.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c3b221d39b259cc60755364bda17bb758e5d87cd
--- /dev/null
+++ b/src/redux/modelElements/modelElements.constants.ts
@@ -0,0 +1 @@
+export const MODEL_ELEMENTS_FETCHING_ERROR_PREFIX = 'Failed to fetch model elements';
diff --git a/src/redux/modelElements/modelElements.mock.ts b/src/redux/modelElements/modelElements.mock.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5ad63a0231db2dc6257cfa4fede7e11fa8ec65b8
--- /dev/null
+++ b/src/redux/modelElements/modelElements.mock.ts
@@ -0,0 +1,8 @@
+import { DEFAULT_ERROR } from '@/constants/errors';
+import { ModelElementsState } from '@/redux/modelElements/modelElements.types';
+
+export const MODEL_ELEMENTS_INITIAL_STATE_MOCK: ModelElementsState = {
+  data: null,
+  loading: 'idle',
+  error: DEFAULT_ERROR,
+};
diff --git a/src/redux/modelElements/modelElements.reducers.test.ts b/src/redux/modelElements/modelElements.reducers.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d896072208c1657f6d40c7b5fdf352944bbd03a2
--- /dev/null
+++ b/src/redux/modelElements/modelElements.reducers.test.ts
@@ -0,0 +1,83 @@
+/* eslint-disable no-magic-numbers */
+import { apiPath } from '@/redux/apiPath';
+import {
+  ToolkitStoreWithSingleSlice,
+  createStoreInstanceUsingSliceReducer,
+} from '@/utils/createStoreInstanceUsingSliceReducer';
+import { mockNetworkNewAPIResponse } from '@/utils/mockNetworkResponse';
+import { HttpStatusCode } from 'axios';
+import { unwrapResult } from '@reduxjs/toolkit';
+import { ModelElementsState } from '@/redux/modelElements/modelElements.types';
+import modelElementsReducer from '@/redux/modelElements/modelElements.slice';
+import { getModelElements } from '@/redux/modelElements/modelElements.thunks';
+import { modelElementsFixture } from '@/models/fixtures/modelElementsFixture';
+
+const mockedAxiosClient = mockNetworkNewAPIResponse();
+
+const INITIAL_STATE: ModelElementsState = {
+  data: null,
+  loading: 'idle',
+  error: { name: '', message: '' },
+};
+
+describe('model elements reducer', () => {
+  let store = {} as ToolkitStoreWithSingleSlice<ModelElementsState>;
+  beforeEach(() => {
+    store = createStoreInstanceUsingSliceReducer('modelElements', modelElementsReducer);
+  });
+
+  it('should match initial state', () => {
+    const action = { type: 'unknown' };
+
+    expect(modelElementsReducer(undefined, action)).toEqual(INITIAL_STATE);
+  });
+
+  it('should update store after successful getModelElements query', async () => {
+    mockedAxiosClient
+      .onGet(apiPath.getModelElements(0))
+      .reply(HttpStatusCode.Ok, modelElementsFixture);
+
+    const { type } = await store.dispatch(getModelElements(0));
+    const { data, loading, error } = store.getState().modelElements;
+
+    expect(type).toBe('vectorMap/getModelElements/fulfilled');
+    expect(loading).toEqual('succeeded');
+    expect(error).toEqual({ message: '', name: '' });
+    expect(data).toEqual(modelElementsFixture);
+  });
+
+  it('should update store after failed getModelElements query', async () => {
+    mockedAxiosClient.onGet(apiPath.getModelElements(0)).reply(HttpStatusCode.NotFound, []);
+
+    const action = await store.dispatch(getModelElements(0));
+    const { data, loading, error } = store.getState().modelElements;
+
+    expect(action.type).toBe('vectorMap/getModelElements/rejected');
+    expect(() => unwrapResult(action)).toThrow(
+      "Failed to fetch model elements: The page you're looking for doesn't exist. Please verify the URL and try again.",
+    );
+    expect(loading).toEqual('failed');
+    expect(error).toEqual({ message: '', name: '' });
+    expect(data).toEqual(null);
+  });
+
+  it('should update store on loading getModelElements query', async () => {
+    mockedAxiosClient
+      .onGet(apiPath.getModelElements(0))
+      .reply(HttpStatusCode.Ok, modelElementsFixture);
+
+    const modelElementsPromise = store.dispatch(getModelElements(0));
+
+    const { data, loading } = store.getState().modelElements;
+    expect(data).toEqual(null);
+    expect(loading).toEqual('pending');
+
+    modelElementsPromise.then(() => {
+      const { data: dataPromiseFulfilled, loading: promiseFulfilled } =
+        store.getState().modelElements;
+
+      expect(dataPromiseFulfilled).toEqual(modelElementsFixture);
+      expect(promiseFulfilled).toEqual('succeeded');
+    });
+  });
+});
diff --git a/src/redux/modelElements/modelElements.reducers.ts b/src/redux/modelElements/modelElements.reducers.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fda618dfa3442ea5c0029e2db6bc2a3b6855edaa
--- /dev/null
+++ b/src/redux/modelElements/modelElements.reducers.ts
@@ -0,0 +1,18 @@
+import { ActionReducerMapBuilder } from '@reduxjs/toolkit';
+import { getModelElements } from '@/redux/modelElements/modelElements.thunks';
+import { ModelElementsState } from '@/redux/modelElements/modelElements.types';
+
+export const getModelElementsReducer = (
+  builder: ActionReducerMapBuilder<ModelElementsState>,
+): void => {
+  builder.addCase(getModelElements.pending, state => {
+    state.loading = 'pending';
+  });
+  builder.addCase(getModelElements.fulfilled, (state, action) => {
+    state.data = action.payload || null;
+    state.loading = 'succeeded';
+  });
+  builder.addCase(getModelElements.rejected, state => {
+    state.loading = 'failed';
+  });
+};
diff --git a/src/redux/modelElements/modelElements.selector.ts b/src/redux/modelElements/modelElements.selector.ts
new file mode 100644
index 0000000000000000000000000000000000000000..54b4a75b00d98355f601dfa2ed2c49c1911f4258
--- /dev/null
+++ b/src/redux/modelElements/modelElements.selector.ts
@@ -0,0 +1,7 @@
+import { createSelector } from '@reduxjs/toolkit';
+import { rootSelector } from '@/redux/root/root.selectors';
+
+export const modelElementsSelector = createSelector(
+  rootSelector,
+  state => state.modelElements.data,
+);
diff --git a/src/redux/modelElements/modelElements.slice.ts b/src/redux/modelElements/modelElements.slice.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b9e7f4119ab512ee9327b6f82854d42b2cd308f1
--- /dev/null
+++ b/src/redux/modelElements/modelElements.slice.ts
@@ -0,0 +1,20 @@
+import { createSlice } from '@reduxjs/toolkit';
+import { getModelElementsReducer } from '@/redux/modelElements/modelElements.reducers';
+import { ModelElementsState } from '@/redux/modelElements/modelElements.types';
+
+const initialState: ModelElementsState = {
+  data: null,
+  loading: 'idle',
+  error: { name: '', message: '' },
+};
+
+export const modelElements = createSlice({
+  name: 'modelElements',
+  initialState,
+  reducers: {},
+  extraReducers: builder => {
+    getModelElementsReducer(builder);
+  },
+});
+
+export default modelElements.reducer;
diff --git a/src/redux/modelElements/modelElements.thunks.test.ts b/src/redux/modelElements/modelElements.thunks.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c5611de7faa4a7de5420a58d5450111a9f0e373d
--- /dev/null
+++ b/src/redux/modelElements/modelElements.thunks.test.ts
@@ -0,0 +1,41 @@
+/* eslint-disable no-magic-numbers */
+import { apiPath } from '@/redux/apiPath';
+import {
+  ToolkitStoreWithSingleSlice,
+  createStoreInstanceUsingSliceReducer,
+} from '@/utils/createStoreInstanceUsingSliceReducer';
+import { mockNetworkNewAPIResponse } from '@/utils/mockNetworkResponse';
+import { HttpStatusCode } from 'axios';
+import { ModelElementsState } from '@/redux/modelElements/modelElements.types';
+import modelElementsReducer from '@/redux/modelElements/modelElements.slice';
+import { getModelElements } from '@/redux/modelElements/modelElements.thunks';
+import { modelElementsFixture } from '@/models/fixtures/modelElementsFixture';
+
+const mockedAxiosClient = mockNetworkNewAPIResponse();
+
+describe('model elements thunks', () => {
+  let store = {} as ToolkitStoreWithSingleSlice<ModelElementsState>;
+  beforeEach(() => {
+    store = createStoreInstanceUsingSliceReducer('modelElements', modelElementsReducer);
+  });
+
+  describe('getModelElements', () => {
+    it('should return data when data response from API is valid', async () => {
+      mockedAxiosClient
+        .onGet(apiPath.getModelElements(0))
+        .reply(HttpStatusCode.Ok, modelElementsFixture);
+
+      const { payload } = await store.dispatch(getModelElements(0));
+      expect(payload).toEqual(modelElementsFixture);
+    });
+
+    it('should return undefined when data response from API is not valid ', async () => {
+      mockedAxiosClient
+        .onGet(apiPath.getModelElements(0))
+        .reply(HttpStatusCode.Ok, { randomProperty: 'randomValue' });
+
+      const { payload } = await store.dispatch(getModelElements(0));
+      expect(payload).toEqual(undefined);
+    });
+  });
+});
diff --git a/src/redux/modelElements/modelElements.thunks.ts b/src/redux/modelElements/modelElements.thunks.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7898db9a04b263622c80cd3192c7ec4417935892
--- /dev/null
+++ b/src/redux/modelElements/modelElements.thunks.ts
@@ -0,0 +1,24 @@
+import { apiPath } from '@/redux/apiPath';
+import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
+import { createAsyncThunk } from '@reduxjs/toolkit';
+import { ThunkConfig } from '@/types/store';
+import { getError } from '@/utils/error-report/getError';
+import { axiosInstanceNewAPI } from '@/services/api/utils/axiosInstance';
+import { modelElementsSchema } from '@/models/modelElementsSchema';
+import { ModelElements } from '@/types/models';
+import { MODEL_ELEMENTS_FETCHING_ERROR_PREFIX } from '@/redux/modelElements/modelElements.constants';
+
+export const getModelElements = createAsyncThunk<ModelElements | undefined, number, ThunkConfig>(
+  'vectorMap/getModelElements',
+  async (modelId: number) => {
+    try {
+      const response = await axiosInstanceNewAPI.get<ModelElements>(
+        apiPath.getModelElements(modelId),
+      );
+      const isDataValid = validateDataUsingZodSchema(response.data, modelElementsSchema);
+      return isDataValid ? response.data : undefined;
+    } catch (error) {
+      return Promise.reject(getError({ error, prefix: MODEL_ELEMENTS_FETCHING_ERROR_PREFIX }));
+    }
+  },
+);
diff --git a/src/redux/modelElements/modelElements.types.ts b/src/redux/modelElements/modelElements.types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0dfdb426840f0a55ec4429ca78f06587d23f0f5f
--- /dev/null
+++ b/src/redux/modelElements/modelElements.types.ts
@@ -0,0 +1,4 @@
+import { FetchDataState } from '@/types/fetchDataState';
+import { ModelElements } from '@/types/models';
+
+export type ModelElementsState = FetchDataState<ModelElements, null>;
diff --git a/src/redux/models/models.reducers.ts b/src/redux/models/models.reducers.ts
index 4b9f7fc67c95ba1f80df7f907cbc4bb8fc1a3d28..ee7b9a63f5ac133a8b5fcfa6bf6f936698fb1305 100644
--- a/src/redux/models/models.reducers.ts
+++ b/src/redux/models/models.reducers.ts
@@ -1,4 +1,5 @@
-import { ActionReducerMapBuilder } from '@reduxjs/toolkit';
+/* eslint-disable no-magic-numbers */
+import { ActionReducerMapBuilder, PayloadAction } from '@reduxjs/toolkit';
 import { getModels } from './models.thunks';
 import { ModelsState } from './models.types';
 
@@ -15,3 +16,14 @@ export const getModelsReducer = (builder: ActionReducerMapBuilder<ModelsState>):
     // TODO to discuss manage state of failure
   });
 };
+
+export const setModelVectorRenderingReducer = (
+  state: ModelsState,
+  action: PayloadAction<{ vectorRendering: boolean; mapId: number }>,
+): void => {
+  const { payload } = action;
+  const modelIndex = state.data.findIndex(model => model.idObject === payload.mapId);
+  if (modelIndex !== -1) {
+    state.data[modelIndex].vectorRendering = payload.vectorRendering;
+  }
+};
diff --git a/src/redux/models/models.slice.ts b/src/redux/models/models.slice.ts
index 5c969f3e0e1d6eb2b0e5183d3a5bc7b454248f60..48cf86543d6af1dc4ac35c04e4e4c8e8f85ad260 100644
--- a/src/redux/models/models.slice.ts
+++ b/src/redux/models/models.slice.ts
@@ -1,6 +1,6 @@
 import { ModelsState } from '@/redux/models/models.types';
 import { createSlice } from '@reduxjs/toolkit';
-import { getModelsReducer } from './models.reducers';
+import { getModelsReducer, setModelVectorRenderingReducer } from './models.reducers';
 
 const initialState: ModelsState = {
   data: [],
@@ -11,10 +11,14 @@ const initialState: ModelsState = {
 export const modelsSlice = createSlice({
   name: 'models',
   initialState,
-  reducers: {},
+  reducers: {
+    setModelVectorRendering: setModelVectorRenderingReducer,
+  },
   extraReducers: builder => {
     getModelsReducer(builder);
   },
 });
 
+export const { setModelVectorRendering } = modelsSlice.actions;
+
 export default modelsSlice.reducer;
diff --git a/src/redux/root/init.thunks.ts b/src/redux/root/init.thunks.ts
index 20a722afb23f2b73870dea3ee777170a060f1ac9..4e161b6e786697a53f445b4b880fcafab26c3b7b 100644
--- a/src/redux/root/init.thunks.ts
+++ b/src/redux/root/init.thunks.ts
@@ -15,6 +15,7 @@ import {
 import { openSelectProjectModal } from '@/redux/modal/modal.slice';
 import { getProjects } from '@/redux/projects/projects.thunks';
 import { getSubmapConnectionsBioEntity } from '@/redux/bioEntity/thunks/getSubmapConnectionsBioEntity';
+import { getShapes } from '@/redux/shapes/shapes.thunks';
 import { getAllBackgroundsByProjectId } from '../backgrounds/backgrounds.thunks';
 import { getConfiguration, getConfigurationOptions } from '../configuration/configuration.thunks';
 import {
@@ -58,6 +59,7 @@ export const fetchInitialAppData = createAsyncThunk<
     dispatch(getAllBackgroundsByProjectId(PROJECT_ID)),
     dispatch(getAllPublicOverlaysByProjectId(PROJECT_ID)),
     dispatch(getModels()),
+    dispatch(getShapes()),
   ]);
 
   if (queryData.pluginsId) {
diff --git a/src/redux/root/root.fixtures.ts b/src/redux/root/root.fixtures.ts
index e260f230ea8bd4a0060fe58399cb9ef2b9965087..7800ab1931b4009204cc14282bbe5fdaee7e8bf0 100644
--- a/src/redux/root/root.fixtures.ts
+++ b/src/redux/root/root.fixtures.ts
@@ -3,6 +3,8 @@ import { PROJECTS_STATE_INITIAL_MOCK } from '@/redux/projects/projects.mock';
 import { OAUTH_INITIAL_STATE_MOCK } from '@/redux/oauth/oauth.mock';
 import { COMMENT_INITIAL_STATE_MOCK } from '@/redux/comment/comment.mock';
 import { AUTOCOMPLETE_INITIAL_STATE } from '@/redux/autocomplete/autocomplete.constants';
+import { BIO_SHAPES_STATE_INITIAL_MOCK } from '@/redux/shapes/shapes.mock';
+import { MODEL_ELEMENTS_INITIAL_STATE_MOCK } from '@/redux/modelElements/modelElements.mock';
 import { BACKGROUND_INITIAL_STATE_MOCK } from '../backgrounds/background.mock';
 import { BIOENTITY_INITIAL_STATE_MOCK } from '../bioEntity/bioEntity.mock';
 import { CHEMICALS_INITIAL_STATE_MOCK } from '../chemicals/chemicals.mock';
@@ -36,10 +38,12 @@ export const INITIAL_STORE_STATE_MOCK: RootState = {
   autocompleteChemical: AUTOCOMPLETE_INITIAL_STATE,
   search: SEARCH_STATE_INITIAL_MOCK,
   project: PROJECT_STATE_INITIAL_MOCK,
+  shapes: BIO_SHAPES_STATE_INITIAL_MOCK,
   projects: PROJECTS_STATE_INITIAL_MOCK,
   drugs: DRUGS_INITIAL_STATE_MOCK,
   chemicals: CHEMICALS_INITIAL_STATE_MOCK,
   models: MODELS_INITIAL_STATE_MOCK,
+  modelElements: MODEL_ELEMENTS_INITIAL_STATE_MOCK,
   bioEntity: BIOENTITY_INITIAL_STATE_MOCK,
   backgrounds: BACKGROUND_INITIAL_STATE_MOCK,
   drawer: drawerInitialStateMock,
diff --git a/src/redux/shapes/shapes.constants.ts b/src/redux/shapes/shapes.constants.ts
new file mode 100644
index 0000000000000000000000000000000000000000..04d8b1caed90ca32d964ad2a87e3de34c30e8938
--- /dev/null
+++ b/src/redux/shapes/shapes.constants.ts
@@ -0,0 +1 @@
+export const SHAPES_FETCHING_ERROR_PREFIX = 'Failed to fetch shapes';
diff --git a/src/redux/shapes/shapes.mock.ts b/src/redux/shapes/shapes.mock.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d9ee5018d64fe273b524f309299c1c8765666874
--- /dev/null
+++ b/src/redux/shapes/shapes.mock.ts
@@ -0,0 +1,8 @@
+import { DEFAULT_ERROR } from '@/constants/errors';
+import { BioShapesState } from '@/redux/shapes/shapes.types';
+
+export const BIO_SHAPES_STATE_INITIAL_MOCK: BioShapesState = {
+  data: [],
+  loading: 'idle',
+  error: DEFAULT_ERROR,
+};
diff --git a/src/redux/shapes/shapes.reducers.test.ts b/src/redux/shapes/shapes.reducers.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..41b8df566da00a148a623d3627e75f618a2c9512
--- /dev/null
+++ b/src/redux/shapes/shapes.reducers.test.ts
@@ -0,0 +1,76 @@
+import { apiPath } from '@/redux/apiPath';
+import {
+  ToolkitStoreWithSingleSlice,
+  createStoreInstanceUsingSliceReducer,
+} from '@/utils/createStoreInstanceUsingSliceReducer';
+import { mockNetworkNewAPIResponse } from '@/utils/mockNetworkResponse';
+import { HttpStatusCode } from 'axios';
+import { unwrapResult } from '@reduxjs/toolkit';
+import { shapesFixture } from '@/models/fixtures/shapesFixture';
+import shapesReducer from './shapes.slice';
+import { getShapes } from './shapes.thunks';
+import { BioShapesState } from './shapes.types';
+
+const mockedAxiosClient = mockNetworkNewAPIResponse();
+
+const INITIAL_STATE: BioShapesState = {
+  data: [],
+  loading: 'idle',
+  error: { name: '', message: '' },
+};
+
+describe('shapes reducer', () => {
+  let store = {} as ToolkitStoreWithSingleSlice<BioShapesState>;
+  beforeEach(() => {
+    store = createStoreInstanceUsingSliceReducer('shapes', shapesReducer);
+  });
+
+  it('should match initial state', () => {
+    const action = { type: 'unknown' };
+
+    expect(shapesReducer(undefined, action)).toEqual(INITIAL_STATE);
+  });
+
+  it('should update store after succesfull getShapes query', async () => {
+    mockedAxiosClient.onGet(apiPath.getShapes()).reply(HttpStatusCode.Ok, shapesFixture);
+
+    const { type } = await store.dispatch(getShapes());
+    const { data, loading, error } = store.getState().shapes;
+    expect(type).toBe('vectorMap/getShapes/fulfilled');
+    expect(loading).toEqual('succeeded');
+    expect(error).toEqual({ message: '', name: '' });
+    expect(data).toEqual(shapesFixture);
+  });
+
+  it('should update store after failed getShapes query', async () => {
+    mockedAxiosClient.onGet(apiPath.getShapes()).reply(HttpStatusCode.NotFound, []);
+
+    const action = await store.dispatch(getShapes());
+    const { data, loading, error } = store.getState().shapes;
+
+    expect(action.type).toBe('vectorMap/getShapes/rejected');
+    expect(() => unwrapResult(action)).toThrow(
+      "Failed to fetch shapes: The page you're looking for doesn't exist. Please verify the URL and try again.",
+    );
+    expect(loading).toEqual('failed');
+    expect(error).toEqual({ message: '', name: '' });
+    expect(data).toEqual([]);
+  });
+
+  it('should update store on loading getShapes query', async () => {
+    mockedAxiosClient.onGet(apiPath.getShapes()).reply(HttpStatusCode.Ok, shapesFixture);
+
+    const shapesPromise = store.dispatch(getShapes());
+
+    const { data, loading } = store.getState().shapes;
+    expect(data).toEqual([]);
+    expect(loading).toEqual('pending');
+
+    shapesPromise.then(() => {
+      const { data: dataPromiseFulfilled, loading: promiseFulfilled } = store.getState().shapes;
+
+      expect(dataPromiseFulfilled).toEqual(shapesFixture);
+      expect(promiseFulfilled).toEqual('succeeded');
+    });
+  });
+});
diff --git a/src/redux/shapes/shapes.reducers.ts b/src/redux/shapes/shapes.reducers.ts
new file mode 100644
index 0000000000000000000000000000000000000000..72ea84f0fba44c9e14aa8b0076496be48cd6fe5f
--- /dev/null
+++ b/src/redux/shapes/shapes.reducers.ts
@@ -0,0 +1,16 @@
+import { ActionReducerMapBuilder } from '@reduxjs/toolkit';
+import { BioShapesState } from '@/redux/shapes/shapes.types';
+import { getShapes } from '@/redux/shapes/shapes.thunks';
+
+export const getShapesReducer = (builder: ActionReducerMapBuilder<BioShapesState>): void => {
+  builder.addCase(getShapes.pending, state => {
+    state.loading = 'pending';
+  });
+  builder.addCase(getShapes.fulfilled, (state, action) => {
+    state.data = action.payload || [];
+    state.loading = 'succeeded';
+  });
+  builder.addCase(getShapes.rejected, state => {
+    state.loading = 'failed';
+  });
+};
diff --git a/src/redux/shapes/shapes.selectors.ts b/src/redux/shapes/shapes.selectors.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6faab0fe791516a689f72b1b8be01d12298b8220
--- /dev/null
+++ b/src/redux/shapes/shapes.selectors.ts
@@ -0,0 +1,9 @@
+import { createSelector } from '@reduxjs/toolkit';
+import { rootSelector } from '@/redux/root/root.selectors';
+
+export const shapesSelector = createSelector(rootSelector, state => state.shapes);
+
+export const shapeBySBOSelector = createSelector(
+  [shapesSelector, (_state, shapeSBO: string): string => shapeSBO],
+  (shapes, shapeSBO) => (shapes?.data || []).find(({ sboTerm }) => sboTerm === shapeSBO),
+);
diff --git a/src/redux/shapes/shapes.slice.ts b/src/redux/shapes/shapes.slice.ts
new file mode 100644
index 0000000000000000000000000000000000000000..06c08ec04e1a92e70d963c584b47154e013b15a3
--- /dev/null
+++ b/src/redux/shapes/shapes.slice.ts
@@ -0,0 +1,20 @@
+import { BioShapesState } from '@/redux/shapes/shapes.types';
+import { createSlice } from '@reduxjs/toolkit';
+import { getShapesReducer } from '@/redux/shapes/shapes.reducers';
+
+const initialState: BioShapesState = {
+  data: [],
+  loading: 'idle',
+  error: { name: '', message: '' },
+};
+
+export const shapesSlice = createSlice({
+  name: 'shapes',
+  initialState,
+  reducers: {},
+  extraReducers: builder => {
+    getShapesReducer(builder);
+  },
+});
+
+export default shapesSlice.reducer;
diff --git a/src/redux/shapes/shapes.thunks.test.ts b/src/redux/shapes/shapes.thunks.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d4cb142cccf4f31fbb1c82b95c8ea36d6d4119cc
--- /dev/null
+++ b/src/redux/shapes/shapes.thunks.test.ts
@@ -0,0 +1,36 @@
+import { apiPath } from '@/redux/apiPath';
+import {
+  ToolkitStoreWithSingleSlice,
+  createStoreInstanceUsingSliceReducer,
+} from '@/utils/createStoreInstanceUsingSliceReducer';
+import { mockNetworkNewAPIResponse } from '@/utils/mockNetworkResponse';
+import { HttpStatusCode } from 'axios';
+import { BioShapesState } from '@/redux/shapes/shapes.types';
+import { shapesFixture } from '@/models/fixtures/shapesFixture';
+import shapesReducer from './shapes.slice';
+import { getShapes } from './shapes.thunks';
+
+const mockedAxiosClient = mockNetworkNewAPIResponse();
+
+describe('shapes thunks', () => {
+  let store = {} as ToolkitStoreWithSingleSlice<BioShapesState>;
+  beforeEach(() => {
+    store = createStoreInstanceUsingSliceReducer('shapes', shapesReducer);
+  });
+  describe('getShapes', () => {
+    it('should return data when data response from API is valid', async () => {
+      mockedAxiosClient.onGet(apiPath.getShapes()).reply(HttpStatusCode.Ok, shapesFixture);
+
+      const { payload } = await store.dispatch(getShapes());
+      expect(payload).toEqual(shapesFixture);
+    });
+    it('should return undefined when data response from API is not valid ', async () => {
+      mockedAxiosClient
+        .onGet(apiPath.getShapes())
+        .reply(HttpStatusCode.Ok, { randomProperty: 'randomValue' });
+
+      const { payload } = await store.dispatch(getShapes());
+      expect(payload).toEqual(undefined);
+    });
+  });
+});
diff --git a/src/redux/shapes/shapes.thunks.ts b/src/redux/shapes/shapes.thunks.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2bb0b7ecf5d6d33c63eb60b946a1d1e7ecc58822
--- /dev/null
+++ b/src/redux/shapes/shapes.thunks.ts
@@ -0,0 +1,24 @@
+import { bioShapeSchema } from '@/models/bioShapeSchema';
+import { apiPath } from '@/redux/apiPath';
+import { BioShape } from '@/types/models';
+import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
+import { createAsyncThunk } from '@reduxjs/toolkit';
+import { z } from 'zod';
+import { ThunkConfig } from '@/types/store';
+import { getError } from '@/utils/error-report/getError';
+import { SHAPES_FETCHING_ERROR_PREFIX } from '@/redux/shapes/shapes.constants';
+import { axiosInstanceNewAPI } from '@/services/api/utils/axiosInstance';
+
+export const getShapes = createAsyncThunk<BioShape[] | undefined, void, ThunkConfig>(
+  'vectorMap/getShapes',
+  async () => {
+    try {
+      const response = await axiosInstanceNewAPI.get<BioShape[]>(apiPath.getShapes());
+      const isDataValid = validateDataUsingZodSchema(response.data, z.array(bioShapeSchema));
+
+      return isDataValid ? response.data : undefined;
+    } catch (error) {
+      return Promise.reject(getError({ error, prefix: SHAPES_FETCHING_ERROR_PREFIX }));
+    }
+  },
+);
diff --git a/src/redux/shapes/shapes.types.ts b/src/redux/shapes/shapes.types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e4b006f1906eb3ef0003fe127c6f61188ba907fd
--- /dev/null
+++ b/src/redux/shapes/shapes.types.ts
@@ -0,0 +1,4 @@
+import { FetchDataState } from '@/types/fetchDataState';
+import { BioShape } from '@/types/models';
+
+export type BioShapesState = FetchDataState<BioShape[], []>;
diff --git a/src/redux/store.ts b/src/redux/store.ts
index f11f6e57248b0a405241aa54ee29d321e1b29ffc..2df3467569d8cc85a234814cc001060be1ca5c99 100644
--- a/src/redux/store.ts
+++ b/src/redux/store.ts
@@ -10,6 +10,8 @@ import drugsReducer from '@/redux/drugs/drugs.slice';
 import mapReducer from '@/redux/map/map.slice';
 import modalReducer from '@/redux/modal/modal.slice';
 import modelsReducer from '@/redux/models/models.slice';
+import shapesReducer from '@/redux/shapes/shapes.slice';
+import modelElementsReducer from '@/redux/modelElements/modelElements.slice';
 import oauthReducer from '@/redux/oauth/oauth.slice';
 import overlayBioEntityReducer from '@/redux/overlayBioEntity/overlayBioEntity.slice';
 import overlaysReducer from '@/redux/overlays/overlays.slice';
@@ -59,6 +61,8 @@ export const reducers = {
   backgrounds: backgroundsReducer,
   overlays: overlaysReducer,
   models: modelsReducer,
+  shapes: shapesReducer,
+  modelElements: modelElementsReducer,
   reactions: reactionsReducer,
   contextMenu: contextMenuReducer,
   cookieBanner: cookieBannerReducer,
diff --git a/src/shared/Switch/Switch.component.tsx b/src/shared/Switch/Switch.component.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..355e84b92d9aaf3e49ab8f0c41d01e8ee28037b8
--- /dev/null
+++ b/src/shared/Switch/Switch.component.tsx
@@ -0,0 +1,71 @@
+import { twMerge } from 'tailwind-merge';
+import { useEffect, useState } from 'react';
+
+type VariantStyle = 'primary' | 'secondary' | 'ghost' | 'quiet';
+
+export interface SwitchProps {
+  variantStyles?: VariantStyle;
+  isChecked?: boolean;
+  onToggle?: (checked: boolean) => void;
+}
+
+const variants = {
+  primary: {
+    switch: 'bg-greyscale-700',
+    circle: 'bg-white-pearl',
+  },
+  secondary: {
+    switch: 'bg-primary-100',
+    circle: 'bg-white-pearl',
+  },
+  ghost: {
+    switch: 'bg-greyscale-600',
+    circle: 'bg-white-pearl',
+  },
+  quiet: {
+    switch: 'bg-greyscale-500',
+    circle: 'bg-white-pearl',
+  },
+} as const;
+
+export const Switch = ({
+  variantStyles = 'primary',
+  isChecked = false,
+  onToggle,
+}: SwitchProps): JSX.Element => {
+  const [checked, setChecked] = useState(isChecked);
+
+  useEffect(() => {
+    setChecked(isChecked);
+  }, [isChecked]);
+
+  const handleToggle = (): void => {
+    const newChecked = !checked;
+    setChecked(newChecked);
+    if (onToggle) {
+      onToggle(newChecked);
+    }
+  };
+
+  return (
+    <button
+      type="button"
+      className={twMerge(
+        'relative inline-flex h-5 w-10 cursor-pointer rounded-full transition-colors duration-300 ease-in-out',
+        variants[variantStyles].switch,
+        checked ? 'bg-primary-600' : '',
+      )}
+      onClick={handleToggle}
+    >
+      <span
+        className={twMerge(
+          'absolute left-0 top-0 h-5 w-5 rounded-full transition-transform duration-300 ease-in-out',
+          variants[variantStyles].circle,
+          checked ? 'translate-x-6' : 'translate-x-0',
+        )}
+      />
+    </button>
+  );
+};
+
+Switch.displayName = 'Switch';
diff --git a/src/shared/Switch/index.tsx b/src/shared/Switch/index.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..ce66469a5ea5baf250a33b9e4b0ef1a9e25036d8
--- /dev/null
+++ b/src/shared/Switch/index.tsx
@@ -0,0 +1 @@
+export { Switch } from './Switch.component';
diff --git a/src/types/models.ts b/src/types/models.ts
index c55955ac291dc9571cd2879f730777ebb663682e..878d99dfe3b4a8316dbfd11db5cbacd1c3eae2f7 100644
--- a/src/types/models.ts
+++ b/src/types/models.ts
@@ -65,6 +65,9 @@ import { commentSchema } from '@/models/commentSchema';
 import { userSchema } from '@/models/userSchema';
 import { javaStacktraceSchema } from '@/models/javaStacktraceSchema';
 import { oauthSchema } from '@/models/oauthSchema';
+import { bioShapeSchema } from '@/models/bioShapeSchema';
+import { shapeSchema } from '@/models/shapeSchema';
+import { modelElementsSchema } from '@/models/modelElementsSchema';
 
 export type Project = z.infer<typeof projectSchema>;
 export type OverviewImageView = z.infer<typeof overviewImageView>;
@@ -72,6 +75,9 @@ export type OverviewImageLink = z.infer<typeof overviewImageLink>;
 export type OverviewImageLinkImage = z.infer<typeof overviewImageLinkImage>;
 export type OverviewImageLinkModel = z.infer<typeof overviewImageLinkModel>;
 export type MapModel = z.infer<typeof mapModelSchema>;
+export type BioShape = z.infer<typeof bioShapeSchema>;
+export type ModelElements = z.infer<typeof modelElementsSchema>;
+export type Shape = z.infer<typeof shapeSchema>;
 export type MapOverlay = z.infer<typeof mapOverlay>;
 export type MapBackground = z.infer<typeof mapBackground>;
 export type Organism = z.infer<typeof organism>;