diff --git a/src/components/FunctionalArea/NavBar/NavBar.component.tsx b/src/components/FunctionalArea/NavBar/NavBar.component.tsx
index 7eb7b2b0f556a4f4fe4e7a805ed00a916a97cd8a..644b830ccbb8784355cb4100ab19b12332e38d03 100644
--- a/src/components/FunctionalArea/NavBar/NavBar.component.tsx
+++ b/src/components/FunctionalArea/NavBar/NavBar.component.tsx
@@ -50,6 +50,14 @@ export const NavBar = (): JSX.Element => {
     }
   };
 
+  const toggleDrawerLayers = (): void => {
+    if (store.getState().drawer.isOpen && store.getState().drawer.drawerName === 'layers') {
+      dispatch(closeDrawer());
+    } else {
+      dispatch(openDrawer('layers'));
+    }
+  };
+
   const toggleDrawerLegend = (): void => {
     if (store.getState().legend.isOpen) {
       dispatch(closeLegend());
@@ -77,6 +85,7 @@ export const NavBar = (): JSX.Element => {
           </a>
           <IconButton icon="plugin" onClick={toggleDrawerPlugins} title="Available plugins" />
           <IconButton icon="export" onClick={toggleDrawerExport} title="Export" />
+          <IconButton icon="layers" onClick={toggleDrawerLayers} title="Layers" />
         </div>
         <div className="flex flex-col gap-[10px]">
           <IconButton icon="legend" onClick={toggleDrawerLegend} title="Legend" />
diff --git a/src/components/Map/Drawer/Drawer.component.tsx b/src/components/Map/Drawer/Drawer.component.tsx
index 71f10406f94ba978b951b7649e921f4c6013fa53..b18e949fed1a9db8afe490c00ce6f3feb175f098 100644
--- a/src/components/Map/Drawer/Drawer.component.tsx
+++ b/src/components/Map/Drawer/Drawer.component.tsx
@@ -3,6 +3,7 @@ import { drawerSelector } from '@/redux/drawer/drawer.selectors';
 import { useAppSelector } from '@/redux/hooks/useAppSelector';
 import { twMerge } from 'tailwind-merge';
 import { CommentDrawer } from '@/components/Map/Drawer/CommentDrawer';
+import { LayersDrawer } from '@/components/Map/Drawer/LayersDrawer/LayersDrawer.component';
 import { AvailablePluginsDrawer } from './AvailablePluginsDrawer';
 import { BioEntityDrawer } from './BioEntityDrawer/BioEntityDrawer.component';
 import { ExportDrawer } from './ExportDrawer';
@@ -32,6 +33,7 @@ export const Drawer = (): JSX.Element => {
       {isOpen && drawerName === 'export' && <ExportDrawer />}
       {isOpen && drawerName === 'available-plugins' && <AvailablePluginsDrawer />}
       {isOpen && drawerName === 'comment' && <CommentDrawer />}
+      {isOpen && drawerName === 'layers' && <LayersDrawer />}
     </div>
   );
 };
diff --git a/src/components/Map/Drawer/LayersDrawer/LayersDrawer.component.test.tsx b/src/components/Map/Drawer/LayersDrawer/LayersDrawer.component.test.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..e40ea7514e5274b5ffdb0aa00c9b8644a2662376
--- /dev/null
+++ b/src/components/Map/Drawer/LayersDrawer/LayersDrawer.component.test.tsx
@@ -0,0 +1,46 @@
+import {
+  InitialStoreState,
+  getReduxWrapperWithStore,
+} from '@/utils/testing/getReduxWrapperWithStore';
+import { StoreType } from '@/redux/store';
+import { render, screen } from '@testing-library/react';
+import { openedExportDrawerFixture } from '@/redux/drawer/drawerFixture';
+import { LayersDrawer } from '@/components/Map/Drawer/LayersDrawer/LayersDrawer.component';
+
+const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => {
+  const { Wrapper, store } = getReduxWrapperWithStore(initialStoreState);
+
+  return (
+    render(
+      <Wrapper>
+        <LayersDrawer />
+      </Wrapper>,
+    ),
+    {
+      store,
+    }
+  );
+};
+
+describe('ExportDrawer - component', () => {
+  it('should display drawer heading', () => {
+    renderComponent();
+
+    expect(screen.getByText('Layers')).toBeInTheDocument();
+  });
+
+  it('should close drawer after clicking close button', () => {
+    const { store } = renderComponent({
+      drawer: openedExportDrawerFixture,
+    });
+    const closeButton = screen.getByRole('close-drawer-button');
+
+    closeButton.click();
+
+    const {
+      drawer: { isOpen },
+    } = store.getState();
+
+    expect(isOpen).toBe(false);
+  });
+});
diff --git a/src/components/Map/Drawer/LayersDrawer/LayersDrawer.component.tsx b/src/components/Map/Drawer/LayersDrawer/LayersDrawer.component.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..7e6142984a4a9887d16fa281dec26a75695f8f41
--- /dev/null
+++ b/src/components/Map/Drawer/LayersDrawer/LayersDrawer.component.tsx
@@ -0,0 +1,31 @@
+import { useAppSelector } from '@/redux/hooks/useAppSelector';
+import { DrawerHeading } from '@/shared/DrawerHeading';
+import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
+import { layersSelector, layersVisibilitySelector } from '@/redux/layers/layers.selectors';
+import { Switch } from '@/shared/Switch';
+import { setLayerVisibility } from '@/redux/layers/layers.slice';
+
+export const LayersDrawer = (): JSX.Element => {
+  const layers = useAppSelector(layersSelector);
+  const layersVisibility = useAppSelector(layersVisibilitySelector);
+  const dispatch = useAppDispatch();
+
+  return (
+    <div data-testid="layers-drawer" className="h-full max-h-full">
+      <DrawerHeading title="Layers" />
+      <div className="flex h-[calc(100%-93px)] max-h-[calc(100%-93px)] flex-col overflow-y-auto px-6">
+        {layers.map(layer => (
+          <div key={layer.details.id} className="flex items-center justify-between border-b p-4">
+            <h1>{layer.details.name}</h1>
+            <Switch
+              isChecked={layersVisibility[layer.details.layerId]}
+              onToggle={value =>
+                dispatch(setLayerVisibility({ visible: value, layerId: layer.details.layerId }))
+              }
+            />
+          </div>
+        ))}
+      </div>
+    </div>
+  );
+};
diff --git a/src/components/Map/Drawer/LayersDrawer/index.ts b/src/components/Map/Drawer/LayersDrawer/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c608bacebc5f5ee5b6f5863d44ecfca878b67bae
--- /dev/null
+++ b/src/components/Map/Drawer/LayersDrawer/index.ts
@@ -0,0 +1 @@
+export { LayersDrawer } from './LayersDrawer.component';
diff --git a/src/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants.ts b/src/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants.ts
index d038cdf39f083a21b48dad564fcd513094c0adc2..31cae9f134bb5f8d1f4a3d9df4570604782aaa4f 100644
--- a/src/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants.ts
@@ -1,4 +1,10 @@
-import { ColorObject } from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.types';
+import {
+  ColorObject,
+  EllipseCenter,
+  EllipseRadius,
+  ShapeCurvePoint,
+  ShapePoint,
+} from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.types';
 
 export const WHITE_COLOR: ColorObject = {
   alpha: 255,
@@ -9,3 +15,153 @@ export const BLACK_COLOR: ColorObject = {
   alpha: 255,
   rgb: -16777216,
 };
+
+export const COMPARTMENT_SQUARE_POINTS: Array<ShapePoint | ShapeCurvePoint> = [
+  {
+    type: 'REL_ABS_POINT',
+    absoluteX: 10.0,
+    absoluteY: 0.0,
+    relativeX: 0.0,
+    relativeY: 0.0,
+    relativeHeightForX: null,
+    relativeWidthForY: null,
+  },
+  {
+    type: 'REL_ABS_POINT',
+    absoluteX: -10.0,
+    absoluteY: 0.0,
+    relativeX: 100.0,
+    relativeY: 0.0,
+    relativeHeightForX: null,
+    relativeWidthForY: null,
+  },
+  {
+    type: 'REL_ABS_BEZIER_POINT',
+    absoluteX1: 0.0,
+    absoluteY1: 10.0,
+    relativeX1: 100.0,
+    relativeY1: 0.0,
+    relativeHeightForX1: null,
+    relativeWidthForY1: null,
+    absoluteX2: -5.0,
+    absoluteY2: 0.0,
+    relativeX2: 100.0,
+    relativeY2: 0.0,
+    relativeHeightForX2: null,
+    relativeWidthForY2: null,
+    absoluteX3: 0.0,
+    absoluteY3: 5.0,
+    relativeX3: 100.0,
+    relativeY3: 0.0,
+    relativeHeightForX3: null,
+    relativeWidthForY3: null,
+  },
+  {
+    type: 'REL_ABS_POINT',
+    absoluteX: 0.0,
+    absoluteY: -10.0,
+    relativeX: 100.0,
+    relativeY: 100.0,
+    relativeHeightForX: null,
+    relativeWidthForY: null,
+  },
+  {
+    type: 'REL_ABS_BEZIER_POINT',
+    absoluteX1: -10.0,
+    absoluteY1: 0.0,
+    relativeX1: 100.0,
+    relativeY1: 100.0,
+    relativeHeightForX1: null,
+    relativeWidthForY1: null,
+    absoluteX2: 0.0,
+    absoluteY2: -5.0,
+    relativeX2: 100.0,
+    relativeY2: 100.0,
+    relativeHeightForX2: null,
+    relativeWidthForY2: null,
+    absoluteX3: -5.0,
+    absoluteY3: 0.0,
+    relativeX3: 100.0,
+    relativeY3: 100.0,
+    relativeHeightForX3: null,
+    relativeWidthForY3: null,
+  },
+  {
+    type: 'REL_ABS_POINT',
+    absoluteX: 10.0,
+    absoluteY: 0.0,
+    relativeX: 0.0,
+    relativeY: 100.0,
+    relativeHeightForX: null,
+    relativeWidthForY: null,
+  },
+  {
+    type: 'REL_ABS_BEZIER_POINT',
+    absoluteX1: 0.0,
+    absoluteY1: -10.0,
+    relativeX1: 0.0,
+    relativeY1: 100.0,
+    relativeHeightForX1: null,
+    relativeWidthForY1: null,
+    absoluteX2: 5.0,
+    absoluteY2: 0.0,
+    relativeX2: 0.0,
+    relativeY2: 100.0,
+    relativeHeightForX2: null,
+    relativeWidthForY2: null,
+    absoluteX3: 0.0,
+    absoluteY3: -5.0,
+    relativeX3: 0.0,
+    relativeY3: 100.0,
+    relativeHeightForX3: null,
+    relativeWidthForY3: null,
+  },
+  {
+    type: 'REL_ABS_POINT',
+    absoluteX: 0.0,
+    absoluteY: 10.0,
+    relativeX: 0.0,
+    relativeY: 0.0,
+    relativeHeightForX: null,
+    relativeWidthForY: null,
+  },
+  {
+    type: 'REL_ABS_BEZIER_POINT',
+    absoluteX1: 10.0,
+    absoluteY1: 0.0,
+    relativeX1: 0.0,
+    relativeY1: 0.0,
+    relativeHeightForX1: null,
+    relativeWidthForY1: null,
+    absoluteX2: 0.0,
+    absoluteY2: 5.0,
+    relativeX2: 0.0,
+    relativeY2: 0.0,
+    relativeHeightForX2: null,
+    relativeWidthForY2: null,
+    absoluteX3: 5.0,
+    absoluteY3: 0.0,
+    relativeX3: 0.0,
+    relativeY3: 0.0,
+    relativeHeightForX3: null,
+    relativeWidthForY3: null,
+  },
+];
+
+export const COMPARTMENT_CIRCLE_CENTER: EllipseCenter = {
+  type: 'REL_ABS_POINT',
+  absoluteX: 0.0,
+  absoluteY: 0.0,
+  relativeX: 50.0,
+  relativeY: 50.0,
+  relativeHeightForX: null,
+  relativeWidthForY: null,
+};
+
+export const COMPARTMENT_CIRCLE_RADIUS: EllipseRadius = {
+  type: 'REL_ABS_RADIUS',
+  absoluteX: 0.0,
+  absoluteY: 0.0,
+  relativeX: 50.0,
+  relativeY: 50.0,
+};
diff --git a/src/components/Map/MapViewer/MapViewerVector/MapViewerVector.types.ts b/src/components/Map/MapViewer/MapViewerVector/MapViewerVector.types.ts
index 78e365e3a1fc14eccc28c28ec737740f1f77519b..4c5ee626be061e6f81432f40e596f75e4a1d0c4c 100644
--- a/src/components/Map/MapViewer/MapViewerVector/MapViewerVector.types.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/MapViewerVector.types.ts
@@ -45,3 +45,21 @@ export type ShapeCurvePoint = {
   relativeHeightForX3: number | null;
   relativeWidthForY3: number | null;
 };
+
+export type EllipseCenter = {
+  type: string;
+  absoluteX: number;
+  absoluteY: number;
+  relativeX: number;
+  relativeY: number;
+  relativeHeightForX: number | null;
+  relativeWidthForY: number | null;
+};
+
+export type EllipseRadius = {
+  type: string;
+  absoluteX: number;
+  absoluteY: number;
+  relativeX: number;
+  relativeY: number;
+};
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/config/additionalLayers/useOlMapAdditionalLayers.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/config/additionalLayers/useOlMapAdditionalLayers.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..68151ba06537964a865a132937e502a0c894dc08
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/config/additionalLayers/useOlMapAdditionalLayers.test.ts
@@ -0,0 +1,25 @@
+/* eslint-disable no-magic-numbers */
+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 { useOlMapAdditionalLayers } from '@/components/Map/MapViewer/MapViewerVector/utils/config/additionalLayers/useOlMapAdditionalLayers';
+
+describe('useOlMapAdditionalLayers - 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(() => useOlMapAdditionalLayers(mapInstance), {
+      wrapper: Wrapper,
+    });
+
+    expect(result.current).toBeInstanceOf(Array<VectorLayer>);
+  });
+});
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/config/additionalLayers/useOlMapAdditionalLayers.ts b/src/components/Map/MapViewer/MapViewerVector/utils/config/additionalLayers/useOlMapAdditionalLayers.ts
new file mode 100644
index 0000000000000000000000000000000000000000..98a4276aea101f2916ace06afc65d05e5c1c356f
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/config/additionalLayers/useOlMapAdditionalLayers.ts
@@ -0,0 +1,65 @@
+/* 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 { useSelector } from 'react-redux';
+import { currentModelIdSelector } from '@/redux/models/models.selectors';
+import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
+import { getLayers } from '@/redux/layers/layers.thunks';
+import { layersSelector, layersVisibilitySelector } from '@/redux/layers/layers.selectors';
+import { usePointToProjection } from '@/utils/map/usePointToProjection';
+import { MapInstance } from '@/types/map';
+import { LineString, MultiPolygon, Point } from 'ol/geom';
+import Polygon from 'ol/geom/Polygon';
+import Layer from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/Layer';
+import { arrowTypesSelector, lineTypesSelector } from '@/redux/shapes/shapes.selectors';
+
+export const useOlMapAdditionalLayers = (
+  mapInstance: MapInstance,
+): Array<
+  VectorLayer<
+    VectorSource<Feature<Point> | Feature<Polygon> | Feature<LineString> | Feature<MultiPolygon>>
+  >
+> => {
+  const dispatch = useAppDispatch();
+  const currentModelId = useSelector(currentModelIdSelector);
+  const mapLayers = useSelector(layersSelector);
+  const layersVisibility = useSelector(layersVisibilitySelector);
+  const lineTypes = useSelector(lineTypesSelector);
+  const arrowTypes = useSelector(arrowTypesSelector);
+  const pointToProjection = usePointToProjection();
+
+  useEffect(() => {
+    dispatch(getLayers(currentModelId));
+  }, [currentModelId, dispatch]);
+
+  const vectorLayers = useMemo(() => {
+    return mapLayers.map(layer => {
+      const additionalLayer = new Layer({
+        texts: layer.texts,
+        rects: layer.rects,
+        ovals: layer.ovals,
+        lines: layer.lines,
+        visible: layer.details.visible,
+        layerId: layer.details.layerId,
+        lineTypes,
+        arrowTypes,
+        mapInstance,
+        pointToProjection,
+      });
+      return additionalLayer.vectorLayer;
+    });
+  }, [arrowTypes, lineTypes, mapInstance, mapLayers, pointToProjection]);
+
+  useEffect(() => {
+    vectorLayers.forEach(layer => {
+      const layerId = layer.get('id');
+      if (layerId && layersVisibility[layerId] !== undefined) {
+        layer.setVisible(layersVisibility[layerId]);
+      }
+    });
+  }, [layersVisibility, vectorLayers]);
+
+  return vectorLayers;
+};
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/useOlMapReactionsLayer.ts b/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/useOlMapReactionsLayer.ts
index 9fe39ef0a5c20fccd577f51cb780624d37f57beb..9bc0d80919ec75843004806eb9e57fed2c5bc96c 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/useOlMapReactionsLayer.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/useOlMapReactionsLayer.ts
@@ -4,9 +4,9 @@ import VectorLayer from 'ol/layer/Vector';
 import VectorSource from 'ol/source/Vector';
 import { useEffect, useMemo } from 'react';
 import { usePointToProjection } from '@/utils/map/usePointToProjection';
-import MapElement from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/MapElement';
+import MapElement from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement';
 import { useSelector } from 'react-redux';
-import { shapesSelector } from '@/redux/shapes/shapes.selectors';
+import { bioShapesSelector, lineTypesSelector } from '@/redux/shapes/shapes.selectors';
 import { MapInstance } from '@/types/map';
 import {
   HorizontalAlign,
@@ -16,6 +16,9 @@ import { modelElementsSelector } from '@/redux/modelElements/modelElements.selec
 import { currentModelIdSelector } from '@/redux/models/models.selectors';
 import { getModelElements } from '@/redux/modelElements/modelElements.thunks';
 import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
+import CompartmentSquare from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentSquare';
+import CompartmentCircle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentCircle';
+import { ModelElement } from '@/types/models';
 
 export const useOlMapReactionsLayer = ({
   mapInstance,
@@ -30,14 +33,18 @@ export const useOlMapReactionsLayer = ({
   }, [currentModelId, dispatch]);
 
   const pointToProjection = usePointToProjection();
-  const shapes = useSelector(shapesSelector);
+  const shapes = useSelector(bioShapesSelector);
+  const lineTypes = useSelector(lineTypesSelector);
 
-  const elements = useMemo(() => {
-    if (modelElements) {
-      return modelElements.content.map(element => {
-        const shape = shapes.data.find(bioShape => bioShape.sboTerm === element.sboTerm);
-        if (shape) {
-          return new MapElement({
+  const elements: Array<MapElement | CompartmentCircle | CompartmentSquare> = useMemo(() => {
+    if (!modelElements || !shapes) return [];
+
+    const validElements: Array<MapElement | CompartmentCircle | CompartmentSquare> = [];
+    modelElements.content.forEach((element: ModelElement) => {
+      const shape = shapes.find(bioShape => bioShape.sboTerm === element.sboTerm);
+      if (shape) {
+        validElements.push(
+          new MapElement({
             shapes: shape.shapes,
             x: element.x,
             y: element.y,
@@ -49,28 +56,59 @@ export const useOlMapReactionsLayer = ({
             height: element.height,
             zIndex: element.z,
             lineWidth: element.lineWidth,
+            lineType: element.borderLineType,
             fontColor: element.fontColor,
             fillColor: element.fillColor,
             borderColor: element.borderColor,
             nameVerticalAlign: element.nameVerticalAlign as VerticalAlign,
             nameHorizontalAlign: element.nameHorizontalAlign as HorizontalAlign,
+            homodimer: element.homodimer,
+            activity: element.activity,
             text: element.name,
+            fontSize: element.fontSize,
             pointToProjection,
             mapInstance,
             modifications: element.modificationResidues,
-            bioShapes: shapes.data,
-          });
+            lineTypes,
+            bioShapes: shapes,
+          }),
+        );
+      } else if (element.sboTerm === 'SBO:0000290') {
+        const compartmentProps = {
+          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,
+          innerWidth: element.innerWidth,
+          outerWidth: element.outerWidth,
+          thickness: element.thickness,
+          fontColor: element.fontColor,
+          fillColor: element.fillColor,
+          borderColor: element.borderColor,
+          nameVerticalAlign: element.nameVerticalAlign as VerticalAlign,
+          nameHorizontalAlign: element.nameHorizontalAlign as HorizontalAlign,
+          text: element.name,
+          fontSize: element.fontSize,
+          pointToProjection,
+          mapInstance,
+        };
+        if (element.shape === 'OVAL_COMPARTMENT') {
+          validElements.push(new CompartmentCircle(compartmentProps));
+        } else if (element.shape === 'SQUARE_COMPARTMENT') {
+          validElements.push(new CompartmentSquare(compartmentProps));
         }
-        return undefined;
-      });
-    }
-    return [];
-  }, [mapInstance, pointToProjection, shapes.data, modelElements]);
+      }
+    });
+    return validElements;
+  }, [modelElements, shapes, pointToProjection, mapInstance, lineTypes]);
 
   const features = useMemo(() => {
-    return elements
-      .filter((element): element is MapElement => element !== undefined)
-      .map(element => element.multiPolygonFeature);
+    return elements.map(element => element.multiPolygonFeature);
   }, [elements]);
 
   const vectorSource = useMemo(() => {
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/config/useOlMapVectorLayers.ts b/src/components/Map/MapViewer/MapViewerVector/utils/config/useOlMapVectorLayers.ts
index 52cf8ab0c4b70dad32e1733178ba511edd77ec9e..7ebb6e709f662cef89e30112e052bad29c871324 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/config/useOlMapVectorLayers.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/config/useOlMapVectorLayers.ts
@@ -1,6 +1,7 @@
 /* eslint-disable no-magic-numbers */
 import { MapInstance } from '@/types/map';
 import { useOlMapWhiteCardLayer } from '@/components/Map/MapViewer/MapViewerVector/utils/config/useOlMapWhiteCardLayer';
+import { useOlMapAdditionalLayers } from '@/components/Map/MapViewer/MapViewerVector/utils/config/additionalLayers/useOlMapAdditionalLayers';
 import { MapConfig } from '../../MapViewerVector.types';
 import { useOlMapReactionsLayer } from './reactionsLayer/useOlMapReactionsLayer';
 
@@ -11,6 +12,7 @@ interface UseOlMapLayersInput {
 export const useOlMapVectorLayers = ({ mapInstance }: UseOlMapLayersInput): MapConfig['layers'] => {
   const reactionsLayer = useOlMapReactionsLayer({ mapInstance });
   const whiteCardLayer = useOlMapWhiteCardLayer();
+  const additionalLayers = useOlMapAdditionalLayers(mapInstance);
 
-  return [whiteCardLayer, reactionsLayer];
+  return [whiteCardLayer, reactionsLayer, ...additionalLayers];
 };
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/MapElement.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/MapElement.ts
deleted file mode 100644
index 2439b0d6758613331262a28d19e886c78daa6f89..0000000000000000000000000000000000000000
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/MapElement.ts
+++ /dev/null
@@ -1,201 +0,0 @@
-/* eslint-disable no-magic-numbers */
-import { Style, Text } from 'ol/style';
-import Feature, { FeatureLike } from 'ol/Feature';
-import { 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 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 { BioShape, Modification, 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 MapElementProps = {
-  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;
-  bioShapes?: Array<BioShape>;
-  modifications?: Array<Modification>;
-};
-
-export default class MapElement {
-  textStyle: Style | undefined;
-
-  polygons: Array<Polygon> = [];
-
-  styles: Array<Style> = [];
-
-  polygonsTexts: Array<string> = [];
-
-  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,
-    bioShapes = [],
-    modifications = [],
-  }: MapElementProps) {
-    if (text) {
-      const { textCoords, textStyle } = getText({
-        text,
-        fontSize,
-        x: nameX,
-        y: nameY,
-        width: nameWidth,
-        height: nameHeight,
-        color: rgbToHex(fontColor),
-        zIndex,
-        verticalAlign: nameVerticalAlign,
-        horizontalAlign: nameHorizontalAlign,
-        pointToProjection,
-      });
-      this.styles.push(textStyle);
-      this.polygonsTexts.push(text);
-      this.polygons.push(new Polygon([[textCoords, textCoords]]));
-    }
-
-    const multiPolygon: Array<Polygon> = [];
-    modifications.forEach(modification => {
-      if (modification.state === null) {
-        return;
-      }
-
-      const shape = bioShapes.find(bioShape => bioShape.sboTerm === modification.sboTerm);
-      if (!shape) {
-        return;
-      }
-      const multiPolygonModification = getMultiPolygon({
-        x: modification.x,
-        y: modification.y,
-        width: modification.width,
-        height: modification.height,
-        shapes: shape.shapes,
-        pointToProjection,
-        mirror: modification.direction && modification.direction === 'RIGHT',
-      });
-      multiPolygon.push(...multiPolygonModification.flat());
-      multiPolygonModification.forEach(polygon => {
-        const modificationStyle = new Style({
-          geometry: polygon,
-          stroke: getStroke({ color: rgbToHex(modification.borderColor) }),
-          fill: getFill({ color: rgbToHex(modification.fillColor) }),
-          zIndex: modification.z,
-        });
-        const modificationText = modification.stateAbbreviation
-          ? modification.stateAbbreviation
-          : modification.name;
-        if (modificationText) {
-          modificationStyle.setText(
-            new Text({
-              text: modificationText,
-              font: `${modification.fontSize}px Arial`,
-              textAlign: 'center',
-              textBaseline: 'middle',
-              fill: getFill({ color: '#000' }),
-              overflow: true,
-            }),
-          );
-          this.polygonsTexts.push(modification.name);
-        }
-        this.styles.push(modificationStyle);
-      });
-    });
-
-    const elementMultiPolygon = getMultiPolygon({ x, y, width, height, shapes, pointToProjection });
-    this.polygons = [...this.polygons, ...multiPolygon, ...elementMultiPolygon.flat()];
-
-    elementMultiPolygon.forEach(polygon => {
-      this.styles.push(
-        new Style({
-          geometry: polygon,
-          stroke: getStroke({ color: rgbToHex(borderColor), width: lineWidth }),
-          fill: getFill({ color: rgbToHex(fillColor) }),
-          zIndex,
-        }),
-      );
-    });
-
-    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.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);
-    }
-    let index = 0;
-    this.styles.forEach(style => {
-      if (style.getText()) {
-        if (textScale > 0.3) {
-          style.getText()?.setScale(textScale);
-          style.getText()?.setText(this.polygonsTexts[index]);
-          index += 1;
-        } else {
-          style.getText()?.setText(undefined);
-        }
-      }
-    });
-    return this.styles;
-  }
-}
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getBezierCurve.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getBezierCurve.test.ts
similarity index 100%
rename from src/components/Map/MapViewer/MapViewerVector/utils/shapes/getBezierCurve.test.ts
rename to src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getBezierCurve.test.ts
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getBezierCurve.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getBezierCurve.ts
similarity index 100%
rename from src/components/Map/MapViewer/MapViewerVector/utils/shapes/getBezierCurve.ts
rename to src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getBezierCurve.ts
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCentroid.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getCentroid.test.ts
similarity index 100%
rename from src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCentroid.test.ts
rename to src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getCentroid.test.ts
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCentroid.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getCentroid.ts
similarity index 100%
rename from src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCentroid.ts
rename to src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getCentroid.ts
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCoordsX.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getCoordsX.test.ts
similarity index 100%
rename from src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCoordsX.test.ts
rename to src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getCoordsX.test.ts
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCoordsX.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getCoordsX.ts
similarity index 100%
rename from src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCoordsX.ts
rename to src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getCoordsX.ts
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCoordsY.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getCoordsY.test.ts
similarity index 100%
rename from src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCoordsY.test.ts
rename to src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getCoordsY.test.ts
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCoordsY.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getCoordsY.ts
similarity index 100%
rename from src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCoordsY.ts
rename to src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getCoordsY.ts
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCurveCoords.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getCurveCoords.test.ts
similarity index 96%
rename from src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCurveCoords.test.ts
rename to src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getCurveCoords.test.ts
index 8ce8b569bb7a63899c8e4cea5895ae78d73b775d..d212e9eaa9e2a867253336ad0a1a97aadeede98f 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCurveCoords.test.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getCurveCoords.test.ts
@@ -1,6 +1,6 @@
 /* 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 getCoordsX from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getCoordsX';
+import getCoordsY from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getCoordsY';
 import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
 import { ShapeCurvePoint } from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.types';
 import getCurveCoords from './getCurveCoords';
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCurveCoords.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getCurveCoords.ts
similarity index 95%
rename from src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCurveCoords.ts
rename to src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getCurveCoords.ts
index 2b6956348eafea711c9636ced732b4fa3422d729..ebfed6b651b1ce599cc7a0d053acebcf93771a91 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getCurveCoords.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getCurveCoords.ts
@@ -1,7 +1,7 @@
 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 getCoordsX from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getCoordsX';
+import getCoordsY from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getCoordsY';
 import { ShapeCurvePoint } from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.types';
 
 export default function getCurveCoords({
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getEllipseCoords.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getEllipseCoords.test.ts
similarity index 95%
rename from src/components/Map/MapViewer/MapViewerVector/utils/shapes/getEllipseCoords.test.ts
rename to src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getEllipseCoords.test.ts
index cf6f4c49f75ca81e8f569d17d040f7e55e6c2817..4a0d514a3829c004d7af79ad435d619821b92ebb 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getEllipseCoords.test.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getEllipseCoords.test.ts
@@ -1,6 +1,6 @@
 /* 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 getCoordsX from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getCoordsX';
+import getCoordsY from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getCoordsY';
 import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
 import getEllipseCoords from './getEllipseCoords';
 
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getEllipseCoords.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getEllipseCoords.ts
similarity index 53%
rename from src/components/Map/MapViewer/MapViewerVector/utils/shapes/getEllipseCoords.ts
rename to src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getEllipseCoords.ts
index 5d1e7f668ebabd78939c0af7d9a424c7e10517ac..45a7df697933f76806fabd13bbea2b790e2e2855 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getEllipseCoords.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getEllipseCoords.ts
@@ -1,26 +1,12 @@
 /* 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 getCoordsX from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getCoordsX';
+import getCoordsY from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/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;
-};
+import {
+  EllipseCenter,
+  EllipseRadius,
+} from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.types';
 
 export default function getEllipseCoords({
   x,
@@ -34,31 +20,39 @@ export default function getEllipseCoords({
 }: {
   x: number;
   y: number;
-  center: EllipseCenter;
-  radius: EllipseRadius;
+  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 centerX = x;
+  let centerY = y;
+  let radiusX = width / 2;
+  let radiusY = height / 2;
+  if (center) {
+    centerX = getCoordsX(
+      x,
+      center.absoluteX,
+      center.relativeX,
+      center.relativeHeightForX,
+      height,
+      width,
+    );
+    centerY = getCoordsY(
+      y,
+      center.absoluteY,
+      center.relativeY,
+      center.relativeWidthForY,
+      height,
+      width,
+    );
+  }
+  if (radius) {
+    radiusX = radius.absoluteX + (radius.relativeX * width) / 100;
+    radiusY = radius.absoluteY + (radius.relativeY * height) / 100;
+  }
   let angle;
   let coordsX;
   let coordsY;
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getPolygonCoords.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getPolygonCoords.test.ts
similarity index 95%
rename from src/components/Map/MapViewer/MapViewerVector/utils/shapes/getPolygonCoords.test.ts
rename to src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getPolygonCoords.test.ts
index e1911f7bc6ca8a5e0360c8662d7f76a4be94cbd2..d88a2c0406337584ebac191f2c3da05cc2566b6d 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getPolygonCoords.test.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getPolygonCoords.test.ts
@@ -1,8 +1,8 @@
 /* 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 getCoordsX from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getCoordsX';
+import getCoordsY from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getCoordsY';
+import getCurveCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getCurveCoords';
+import getBezierCurve from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getBezierCurve';
 import {
   ShapePoint,
   ShapeCurvePoint,
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getPolygonCoords.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getPolygonCoords.ts
similarity index 90%
rename from src/components/Map/MapViewer/MapViewerVector/utils/shapes/getPolygonCoords.ts
rename to src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getPolygonCoords.ts
index b60b001608ad7d4c59f898e7973402ada54be259..dac21118d14a9bf5d51b109f0a401fbbf0af8fc0 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getPolygonCoords.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getPolygonCoords.ts
@@ -1,10 +1,10 @@
 /* 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 getBezierCurve from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getBezierCurve';
+import getCoordsX from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getCoordsX';
+import getCoordsY from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getCoordsY';
+import getCurveCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getCurveCoords';
 import {
   ShapeCurvePoint,
   ShapePoint,
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getRotation.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getRotation.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..363add1a3eef27e0422ff5b8f01601c9a7c85f1c
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getRotation.test.ts
@@ -0,0 +1,19 @@
+/* eslint-disable no-magic-numbers */
+import getRotation from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getRotation';
+import { Coordinate } from 'ol/coordinate';
+
+const testCases: Array<[Coordinate, Coordinate, number]> = [
+  [[0, 0], [1, 1], -0.785],
+  [[1, 1], [1, 2], -1.57],
+  [[2, 2], [3, 0], 1.107],
+];
+
+describe('getRotation', () => {
+  it.each(testCases)(
+    'should return the correct rotation for start: %s and end: %s',
+    (start: Coordinate, end: Coordinate, expected: number) => {
+      const result = getRotation(start, end);
+      expect(result).toBeCloseTo(expected);
+    },
+  );
+});
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getRotation.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getRotation.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5a647fcef1ec19a10fe9cf407df883f44141cca1
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getRotation.ts
@@ -0,0 +1,6 @@
+/* eslint-disable no-magic-numbers */
+import { Coordinate } from 'ol/coordinate';
+
+export default function getRotation(start: Coordinate, end: Coordinate): number {
+  return Math.atan2(-end[1] + start[1], end[0] - start[0]);
+}
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/BaseMultiPolygon.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/BaseMultiPolygon.ts
new file mode 100644
index 0000000000000000000000000000000000000000..30ece3e99fcd17b3c7c15715b7a590b8b6368870
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/BaseMultiPolygon.ts
@@ -0,0 +1,185 @@
+/* eslint-disable no-magic-numbers */
+import Polygon from 'ol/geom/Polygon';
+import { Style } from 'ol/style';
+import Feature, { FeatureLike } from 'ol/Feature';
+import { MultiPolygon } from 'ol/geom';
+import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
+import {
+  ColorObject,
+  HorizontalAlign,
+  VerticalAlign,
+} from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.types';
+import { MapInstance } from '@/types/map';
+import getTextStyle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextStyle';
+import { rgbToHex } from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/rgbToHex';
+import getTextCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextCoords';
+
+export interface BaseMapElementProps {
+  x: number;
+  y: number;
+  width: number;
+  height: number;
+  zIndex: number;
+  text: string;
+  fontSize: number;
+  nameX: number;
+  nameY: number;
+  nameWidth: number;
+  nameHeight: number;
+  fontColor: ColorObject;
+  nameVerticalAlign: VerticalAlign;
+  nameHorizontalAlign: HorizontalAlign;
+  fillColor: ColorObject;
+  borderColor: ColorObject;
+  pointToProjection: UsePointToProjectionResult;
+}
+
+export default abstract class BaseMultiPolygon {
+  x: number;
+
+  y: number;
+
+  width: number;
+
+  height: number;
+
+  zIndex: number;
+
+  text: string;
+
+  fontSize: number;
+
+  nameX: number;
+
+  nameY: number;
+
+  nameWidth: number;
+
+  nameHeight: number;
+
+  fontColor: ColorObject;
+
+  nameVerticalAlign: VerticalAlign;
+
+  nameHorizontalAlign: HorizontalAlign;
+
+  fillColor: ColorObject;
+
+  borderColor: ColorObject;
+
+  polygons: Array<Polygon> = [];
+
+  styles: Array<Style> = [];
+
+  polygonsTexts: Array<string> = [];
+
+  multiPolygonFeature: Feature = new Feature();
+
+  pointToProjection: UsePointToProjectionResult;
+
+  constructor({
+    x,
+    y,
+    width,
+    height,
+    zIndex,
+    text,
+    fontSize,
+    nameX,
+    nameY,
+    nameWidth,
+    nameHeight,
+    fontColor,
+    nameVerticalAlign,
+    nameHorizontalAlign,
+    fillColor,
+    borderColor,
+    pointToProjection,
+  }: BaseMapElementProps) {
+    this.x = x;
+    this.y = y;
+    this.width = width;
+    this.height = height;
+    this.zIndex = zIndex;
+    this.text = text;
+    this.fontSize = fontSize;
+    this.nameX = nameX;
+    this.nameY = nameY;
+    this.nameWidth = nameWidth;
+    this.nameHeight = nameHeight;
+    this.fontColor = fontColor;
+    this.nameVerticalAlign = nameVerticalAlign;
+    this.nameHorizontalAlign = nameHorizontalAlign;
+    this.fillColor = fillColor;
+    this.borderColor = borderColor;
+    this.pointToProjection = pointToProjection;
+  }
+
+  protected abstract createPolygons(): void;
+
+  protected drawText(): void {
+    if (this.text) {
+      const textCoords = getTextCoords({
+        x: this.nameX,
+        y: this.nameY,
+        width: this.nameWidth,
+        height: this.nameHeight,
+        fontSize: this.fontSize,
+        verticalAlign: this.nameVerticalAlign,
+        horizontalAlign: this.nameHorizontalAlign,
+        pointToProjection: this.pointToProjection,
+      });
+      const textPolygon = new Polygon([[textCoords, textCoords]]);
+      const textStyle = getTextStyle({
+        text: this.text,
+        fontSize: this.fontSize,
+        color: rgbToHex(this.fontColor),
+        zIndex: this.zIndex,
+        horizontalAlign: this.nameHorizontalAlign,
+      });
+      textStyle.setGeometry(textPolygon);
+      this.styles.push(textStyle);
+      this.polygonsTexts.push(this.text);
+      this.polygons.push(textPolygon);
+    }
+  }
+
+  protected drawMultiPolygonFeature(mapInstance: MapInstance): void {
+    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.multiPolygonFeature.setStyle(this.styleFunction.bind(this));
+  }
+
+  protected styleFunction(feature: FeatureLike, resolution: number): Style | Array<Style> | void {
+    const getTextScale = feature.get('getTextScale');
+    let textScale = 1;
+    if (getTextScale instanceof Function) {
+      textScale = getTextScale(resolution);
+    }
+    let index = 0;
+    this.styles.forEach(style => {
+      if (style.getText()) {
+        if (this.fontSize * textScale > 4) {
+          style.getText()?.setScale(textScale);
+          style.getText()?.setText(this.polygonsTexts[index]);
+          index += 1;
+        } else {
+          style.getText()?.setText(undefined);
+        }
+      }
+    });
+    return this.styles;
+  }
+}
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Compartment.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Compartment.ts
new file mode 100644
index 0000000000000000000000000000000000000000..786f8c1e29588a6f2a0879495b2f6d0012b4c673
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Compartment.ts
@@ -0,0 +1,137 @@
+/* eslint-disable no-magic-numbers */
+import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
+import {
+  ColorObject,
+  HorizontalAlign,
+  VerticalAlign,
+} from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.types';
+import BaseMultiPolygon from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/BaseMultiPolygon';
+import { Coordinate } from 'ol/coordinate';
+import Polygon from 'ol/geom/Polygon';
+import { Style } from 'ol/style';
+import getFill from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getFill';
+import { rgbToHex } from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/rgbToHex';
+import getStroke from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStroke';
+import { MapInstance } from '@/types/map';
+
+export interface CompartmentProps {
+  x: number;
+  y: number;
+  width: number;
+  height: number;
+  thickness: number;
+  outerWidth: number;
+  innerWidth: number;
+  zIndex: number;
+  text: string;
+  fontSize: number;
+  nameX: number;
+  nameY: number;
+  nameWidth: number;
+  nameHeight: number;
+  fontColor: ColorObject;
+  nameVerticalAlign: VerticalAlign;
+  nameHorizontalAlign: HorizontalAlign;
+  fillColor: ColorObject;
+  borderColor: ColorObject;
+  pointToProjection: UsePointToProjectionResult;
+  mapInstance: MapInstance;
+}
+
+export default abstract class Compartment extends BaseMultiPolygon {
+  outerCoords: Array<Coordinate> = [];
+
+  innerCoords: Array<Coordinate> = [];
+
+  outerWidth: number;
+
+  innerWidth: number;
+
+  thickness: number;
+
+  constructor({
+    x,
+    y,
+    width,
+    height,
+    thickness,
+    outerWidth,
+    innerWidth,
+    zIndex,
+    text,
+    fontSize,
+    nameX,
+    nameY,
+    nameWidth,
+    nameHeight,
+    fontColor,
+    nameVerticalAlign,
+    nameHorizontalAlign,
+    fillColor,
+    borderColor,
+    pointToProjection,
+    mapInstance,
+  }: CompartmentProps) {
+    super({
+      x,
+      y,
+      width,
+      height,
+      zIndex,
+      text,
+      fontSize,
+      nameX,
+      nameY,
+      nameWidth,
+      nameHeight,
+      fontColor,
+      nameVerticalAlign,
+      nameHorizontalAlign,
+      fillColor,
+      borderColor,
+      pointToProjection,
+    });
+    this.outerWidth = outerWidth;
+    this.innerWidth = innerWidth;
+    this.thickness = thickness;
+    this.getCompartmentCoords();
+    this.createPolygons();
+    this.drawText();
+    this.drawMultiPolygonFeature(mapInstance);
+  }
+
+  protected abstract getCompartmentCoords(): void;
+
+  protected createPolygons(): void {
+    const framePolygon = new Polygon([this.outerCoords, this.innerCoords]);
+    this.styles.push(
+      new Style({
+        geometry: framePolygon,
+        fill: getFill({ color: rgbToHex({ ...this.fillColor, alpha: 128 }) }),
+        zIndex: this.zIndex,
+      }),
+    );
+    this.polygons.push(framePolygon);
+
+    const outerPolygon = new Polygon([this.outerCoords]);
+    this.styles.push(
+      new Style({
+        geometry: outerPolygon,
+        stroke: getStroke({ color: rgbToHex(this.borderColor), width: this.outerWidth }),
+        zIndex: this.zIndex,
+      }),
+    );
+    this.polygons.push(outerPolygon);
+
+    const innerPolygon = new Polygon([this.innerCoords]);
+    this.styles.push(
+      new Style({
+        geometry: innerPolygon,
+        stroke: getStroke({ color: rgbToHex(this.borderColor), width: this.innerWidth }),
+        fill: getFill({ color: rgbToHex({ ...this.fillColor, alpha: 9 }) }),
+        zIndex: this.zIndex,
+      }),
+    );
+    this.polygons.push(innerPolygon);
+  }
+}
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentCircle.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentCircle.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..47c47d389b5aa9ecc893045687eb015171ce62ea
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentCircle.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 getTextStyle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextStyle';
+import getMultiPolygon from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getMultiPolygon';
+import getStroke from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStroke';
+import getFill from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getFill';
+import { rgbToHex } from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/rgbToHex';
+import View from 'ol/View';
+import {
+  WHITE_COLOR,
+  BLACK_COLOR,
+} from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants';
+import CompartmentCircle, {
+  CompartmentCircleProps,
+} from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentCircle';
+import getEllipseCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getEllipseCoords';
+import getTextCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextCoords';
+
+jest.mock('../text/getTextStyle');
+jest.mock('../text/getTextCoords');
+jest.mock('./getMultiPolygon');
+jest.mock('../style/getStroke');
+jest.mock('../coords/getEllipseCoords');
+jest.mock('../style/getFill');
+jest.mock('../style/rgbToHex');
+
+describe('MapElement', () => {
+  let props: CompartmentCircleProps;
+
+  beforeEach(() => {
+    const dummyElement = document.createElement('div');
+    const mapInstance = new Map({
+      target: dummyElement,
+      view: new View({
+        zoom: 5,
+        minZoom: 3,
+        maxZoom: 7,
+      }),
+    });
+    props = {
+      x: 0,
+      y: 0,
+      width: 100,
+      height: 100,
+      zIndex: 1,
+      fillColor: WHITE_COLOR,
+      borderColor: BLACK_COLOR,
+      fontColor: BLACK_COLOR,
+      innerWidth: 1,
+      outerWidth: 2,
+      thickness: 12,
+      text: 'Test Text',
+      fontSize: 12,
+      nameX: 10,
+      nameY: 20,
+      nameHeight: 30,
+      nameWidth: 40,
+      nameVerticalAlign: 'MIDDLE',
+      nameHorizontalAlign: 'CENTER',
+      pointToProjection: jest.fn(),
+      mapInstance,
+    };
+
+    (getTextStyle as jest.Mock).mockReturnValue(
+      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',
+        }),
+      }),
+    );
+    (getTextCoords as jest.Mock).mockReturnValue([10, 10]);
+    (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');
+    (getEllipseCoords as jest.Mock).mockReturnValue([
+      [10, 10],
+      [20, 20],
+      [30, 30],
+    ]);
+  });
+
+  it('should initialize with correct default properties', () => {
+    const multiPolygon = new CompartmentCircle(props);
+
+    expect(multiPolygon.polygons.length).toBe(4);
+    expect(multiPolygon.multiPolygonFeature).toBeInstanceOf(Feature);
+    expect(multiPolygon.multiPolygonFeature.getGeometry()).toBeInstanceOf(MultiPolygon);
+  });
+
+  it('should apply correct styles to the feature', () => {
+    const multiPolygon = new CompartmentCircle(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);
+    }
+  });
+});
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentCircle.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentCircle.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5665fc2925d96517ac0b9a133fe1da982562c26d
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentCircle.ts
@@ -0,0 +1,113 @@
+/* eslint-disable no-magic-numbers */
+import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
+import { MapInstance } from '@/types/map';
+import {
+  ColorObject,
+  HorizontalAlign,
+  VerticalAlign,
+} from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.types';
+import {
+  BLACK_COLOR,
+  COMPARTMENT_CIRCLE_CENTER,
+  COMPARTMENT_CIRCLE_RADIUS,
+  WHITE_COLOR,
+} from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants';
+import getEllipseCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getEllipseCoords';
+import Compartment from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Compartment';
+
+export type CompartmentCircleProps = {
+  x: number;
+  y: number;
+  width: number;
+  height: number;
+  zIndex: number;
+  fillColor?: ColorObject;
+  borderColor?: ColorObject;
+  fontColor?: ColorObject;
+  innerWidth?: number;
+  outerWidth?: number;
+  thickness?: number;
+  text?: string;
+  fontSize?: number;
+  nameX: number;
+  nameY: number;
+  nameHeight: number;
+  nameWidth: number;
+  nameVerticalAlign?: VerticalAlign;
+  nameHorizontalAlign?: HorizontalAlign;
+  pointToProjection: UsePointToProjectionResult;
+  mapInstance: MapInstance;
+};
+
+export default class CompartmentCircle extends Compartment {
+  constructor({
+    x,
+    y,
+    width,
+    height,
+    zIndex,
+    fillColor = WHITE_COLOR,
+    borderColor = BLACK_COLOR,
+    fontColor = BLACK_COLOR,
+    innerWidth = 1,
+    outerWidth = 2,
+    thickness = 12,
+    text = '',
+    fontSize = 12,
+    nameX,
+    nameY,
+    nameHeight,
+    nameWidth,
+    nameVerticalAlign = 'MIDDLE',
+    nameHorizontalAlign = 'CENTER',
+    pointToProjection,
+    mapInstance,
+  }: CompartmentCircleProps) {
+    super({
+      x,
+      y,
+      width,
+      height,
+      thickness,
+      outerWidth,
+      innerWidth,
+      zIndex,
+      text,
+      fontSize,
+      nameX,
+      nameY,
+      nameWidth,
+      nameHeight,
+      fontColor,
+      nameVerticalAlign,
+      nameHorizontalAlign,
+      fillColor,
+      borderColor,
+      pointToProjection,
+      mapInstance,
+    });
+  }
+
+  protected getCompartmentCoords(): void {
+    this.outerCoords = getEllipseCoords({
+      x: this.x,
+      y: this.y,
+      center: COMPARTMENT_CIRCLE_CENTER,
+      radius: COMPARTMENT_CIRCLE_RADIUS,
+      height: this.height,
+      width: this.width,
+      points: 40,
+      pointToProjection: this.pointToProjection,
+    });
+    this.innerCoords = getEllipseCoords({
+      x: this.x + this.thickness,
+      y: this.y + this.thickness,
+      center: COMPARTMENT_CIRCLE_CENTER,
+      radius: COMPARTMENT_CIRCLE_RADIUS,
+      height: this.height - 2 * this.thickness,
+      width: this.width - 2 * this.thickness,
+      points: 40,
+      pointToProjection: this.pointToProjection,
+    });
+  }
+}
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentSquare.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentSquare.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5fd6ac55e3334d2f42a015635c97fa281f4144e3
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentSquare.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 getTextStyle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextStyle';
+import getMultiPolygon from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getMultiPolygon';
+import getStroke from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStroke';
+import getFill from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getFill';
+import { rgbToHex } from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/rgbToHex';
+import View from 'ol/View';
+import {
+  WHITE_COLOR,
+  BLACK_COLOR,
+} from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants';
+import CompartmentSquare, {
+  CompartmentSquareProps,
+} from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentSquare';
+import getPolygonCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getPolygonCoords';
+import getTextCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextCoords';
+
+jest.mock('../text/getTextStyle');
+jest.mock('./getMultiPolygon');
+jest.mock('../text/getTextCoords');
+jest.mock('../style/getStroke');
+jest.mock('../coords/getPolygonCoords');
+jest.mock('../style/getFill');
+jest.mock('../style/rgbToHex');
+
+describe('MapElement', () => {
+  let props: CompartmentSquareProps;
+
+  beforeEach(() => {
+    const dummyElement = document.createElement('div');
+    const mapInstance = new Map({
+      target: dummyElement,
+      view: new View({
+        zoom: 5,
+        minZoom: 3,
+        maxZoom: 7,
+      }),
+    });
+    props = {
+      x: 0,
+      y: 0,
+      width: 100,
+      height: 100,
+      zIndex: 1,
+      fillColor: WHITE_COLOR,
+      borderColor: BLACK_COLOR,
+      fontColor: BLACK_COLOR,
+      innerWidth: 1,
+      outerWidth: 2,
+      thickness: 12,
+      text: 'Test Text',
+      fontSize: 12,
+      nameX: 10,
+      nameY: 20,
+      nameHeight: 30,
+      nameWidth: 40,
+      nameVerticalAlign: 'MIDDLE',
+      nameHorizontalAlign: 'CENTER',
+      pointToProjection: jest.fn(),
+      mapInstance,
+    };
+
+    (getTextStyle as jest.Mock).mockReturnValue(
+      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',
+        }),
+      }),
+    );
+    (getTextCoords as jest.Mock).mockReturnValue([10, 10]);
+    (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');
+    (getPolygonCoords as jest.Mock).mockReturnValue([
+      [10, 10],
+      [20, 20],
+      [30, 30],
+    ]);
+  });
+
+  it('should initialize with correct default properties', () => {
+    const multiPolygon = new CompartmentSquare(props);
+
+    expect(multiPolygon.polygons.length).toBe(4);
+    expect(multiPolygon.multiPolygonFeature).toBeInstanceOf(Feature);
+    expect(multiPolygon.multiPolygonFeature.getGeometry()).toBeInstanceOf(MultiPolygon);
+  });
+
+  it('should apply correct styles to the feature', () => {
+    const multiPolygon = new CompartmentSquare(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);
+    }
+  });
+});
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentSquare.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentSquare.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d868bc11c118535e1b51057a32da6a45f336a6d1
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentSquare.ts
@@ -0,0 +1,108 @@
+/* eslint-disable no-magic-numbers */
+import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
+import { MapInstance } from '@/types/map';
+import {
+  ColorObject,
+  HorizontalAlign,
+  VerticalAlign,
+} from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.types';
+import {
+  BLACK_COLOR,
+  COMPARTMENT_SQUARE_POINTS,
+  WHITE_COLOR,
+} from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants';
+import getPolygonCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getPolygonCoords';
+import Compartment from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Compartment';
+
+export type CompartmentSquareProps = {
+  x: number;
+  y: number;
+  width: number;
+  height: number;
+  zIndex: number;
+  fillColor?: ColorObject;
+  borderColor?: ColorObject;
+  fontColor?: ColorObject;
+  innerWidth?: number;
+  outerWidth?: number;
+  thickness?: number;
+  text?: string;
+  fontSize?: number;
+  nameX: number;
+  nameY: number;
+  nameHeight: number;
+  nameWidth: number;
+  nameVerticalAlign?: VerticalAlign;
+  nameHorizontalAlign?: HorizontalAlign;
+  pointToProjection: UsePointToProjectionResult;
+  mapInstance: MapInstance;
+};
+
+export default class CompartmentSquare extends Compartment {
+  constructor({
+    x,
+    y,
+    width,
+    height,
+    zIndex,
+    fillColor = WHITE_COLOR,
+    borderColor = BLACK_COLOR,
+    fontColor = BLACK_COLOR,
+    innerWidth = 1,
+    outerWidth = 2,
+    thickness = 12,
+    text = '',
+    fontSize = 12,
+    nameX,
+    nameY,
+    nameHeight,
+    nameWidth,
+    nameVerticalAlign = 'MIDDLE',
+    nameHorizontalAlign = 'CENTER',
+    pointToProjection,
+    mapInstance,
+  }: CompartmentSquareProps) {
+    super({
+      x,
+      y,
+      width,
+      height,
+      thickness,
+      outerWidth,
+      innerWidth,
+      zIndex,
+      text,
+      fontSize,
+      nameX,
+      nameY,
+      nameWidth,
+      nameHeight,
+      fontColor,
+      nameVerticalAlign,
+      nameHorizontalAlign,
+      fillColor,
+      borderColor,
+      pointToProjection,
+      mapInstance,
+    });
+  }
+
+  protected getCompartmentCoords(): void {
+    this.outerCoords = getPolygonCoords({
+      points: COMPARTMENT_SQUARE_POINTS,
+      x: this.x,
+      y: this.y,
+      height: this.height,
+      width: this.width,
+      pointToProjection: this.pointToProjection,
+    });
+    this.innerCoords = getPolygonCoords({
+      points: COMPARTMENT_SQUARE_POINTS,
+      x: this.x + this.thickness,
+      y: this.y + this.thickness,
+      height: this.height - 2 * this.thickness,
+      width: this.width - 2 * this.thickness,
+      pointToProjection: this.pointToProjection,
+    });
+  }
+}
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/MapElement.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement.test.ts
similarity index 79%
rename from src/components/Map/MapViewer/MapViewerVector/utils/shapes/MapElement.test.ts
rename to src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement.test.ts
index 0ed5313bcf38fc80ace22bec8a0982cb420f5760..51aaeb6572a795ed8e191fd4590e03674f5f3c13 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/MapElement.test.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement.test.ts
@@ -2,25 +2,27 @@
 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 getTextStyle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextStyle';
+import getMultiPolygon from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getMultiPolygon';
+import getStroke from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStroke';
+import getFill from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getFill';
+import { rgbToHex } from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/rgbToHex';
 import MapElement, {
   MapElementProps,
-} from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/MapElement';
+} from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement';
 import View from 'ol/View';
 import {
   WHITE_COLOR,
   BLACK_COLOR,
 } from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants';
+import getTextCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextCoords';
 
-jest.mock('./getText');
+jest.mock('../text/getTextStyle');
+jest.mock('../text/getTextCoords');
 jest.mock('./getMultiPolygon');
-jest.mock('./getStroke');
-jest.mock('./getFill');
-jest.mock('./rgbToHex');
+jest.mock('../style/getStroke');
+jest.mock('../style/getFill');
+jest.mock('../style/rgbToHex');
 
 describe('MapElement', () => {
   let props: MapElementProps;
@@ -58,9 +60,8 @@ describe('MapElement', () => {
       mapInstance,
     };
 
-    (getText as jest.Mock).mockReturnValue({
-      textCoords: [0, 0],
-      textStyle: new Style({
+    (getTextStyle as jest.Mock).mockReturnValue(
+      new Style({
         text: new Text({
           text: props.text,
           font: `bold ${props.fontSize}px Arial`,
@@ -72,7 +73,8 @@ describe('MapElement', () => {
           textBaseline: 'middle',
         }),
       }),
-    });
+    );
+    (getTextCoords as jest.Mock).mockReturnValue([10, 10]);
     (getMultiPolygon as jest.Mock).mockReturnValue([
       new Polygon([
         [
@@ -107,14 +109,4 @@ describe('MapElement', () => {
       expect(style).toBeInstanceOf(Style);
     }
   });
-
-  it('should update text style based on resolution', () => {
-    const multiPolygon = new MapElement(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/elements/MapElement.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement.ts
new file mode 100644
index 0000000000000000000000000000000000000000..025ec2d83a1d2068b35dcb627b69c832c45290e6
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement.ts
@@ -0,0 +1,241 @@
+/* eslint-disable no-magic-numbers */
+import { Style, Text } from 'ol/style';
+import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
+import getStroke from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStroke';
+import getFill from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getFill';
+import Polygon from 'ol/geom/Polygon';
+import getMultiPolygon from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getMultiPolygon';
+import { rgbToHex } from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/rgbToHex';
+import { BioShape, LineType, Modification, 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';
+import BaseMultiPolygon from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/BaseMultiPolygon';
+import getStyle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStyle';
+
+export type MapElementProps = {
+  shapes: Array<Shape>;
+  x: number;
+  y: number;
+  width: number;
+  height: number;
+  zIndex: number;
+  fillColor?: ColorObject;
+  borderColor?: ColorObject;
+  fontColor?: ColorObject;
+  lineWidth?: number;
+  lineType?: string;
+  text?: string;
+  fontSize?: number;
+  nameX: number;
+  nameY: number;
+  nameHeight: number;
+  nameWidth: number;
+  nameVerticalAlign?: VerticalAlign;
+  nameHorizontalAlign?: HorizontalAlign;
+  homodimer?: number;
+  activity?: boolean;
+  pointToProjection: UsePointToProjectionResult;
+  mapInstance: MapInstance;
+  bioShapes?: Array<BioShape>;
+  lineTypes?: Array<LineType>;
+  modifications?: Array<Modification>;
+};
+
+export default class MapElement extends BaseMultiPolygon {
+  shapes: Array<Shape>;
+
+  lineWidth: number;
+
+  lineType: string | undefined;
+
+  bioShapes: Array<BioShape>;
+
+  lineTypes: Array<LineType>;
+
+  homodimer: number;
+
+  activity: boolean | undefined;
+
+  modifications: Array<Modification>;
+
+  lineDash: Array<number> = [];
+
+  constructor({
+    shapes,
+    x,
+    y,
+    width,
+    height,
+    zIndex,
+    fillColor = WHITE_COLOR,
+    borderColor = BLACK_COLOR,
+    fontColor = BLACK_COLOR,
+    lineWidth = 1,
+    lineType,
+    text = '',
+    fontSize = 12,
+    nameX,
+    nameY,
+    nameHeight,
+    nameWidth,
+    nameVerticalAlign = 'MIDDLE',
+    nameHorizontalAlign = 'CENTER',
+    homodimer = 1,
+    activity,
+    pointToProjection,
+    mapInstance,
+    bioShapes = [],
+    lineTypes = [],
+    modifications = [],
+  }: MapElementProps) {
+    super({
+      x,
+      y,
+      width,
+      height,
+      zIndex,
+      text,
+      fontSize,
+      nameX,
+      nameY,
+      nameWidth,
+      nameHeight,
+      fontColor,
+      nameVerticalAlign,
+      nameHorizontalAlign,
+      fillColor,
+      borderColor,
+      pointToProjection,
+    });
+    this.shapes = shapes;
+    this.lineWidth = lineWidth;
+    this.lineType = lineType;
+    this.homodimer = homodimer;
+    this.activity = activity;
+    this.bioShapes = bioShapes;
+    this.lineTypes = lineTypes;
+    this.modifications = modifications;
+    this.createPolygons();
+    this.drawText();
+    this.drawMultiPolygonFeature(mapInstance);
+  }
+
+  protected createPolygons(): void {
+    let multiPolygonModifications: Array<Polygon> = [];
+    this.modifications.forEach(modification => {
+      if (modification.state === null) {
+        return;
+      }
+
+      const shape = this.bioShapes.find(bioShape => bioShape.sboTerm === modification.sboTerm);
+      if (!shape) {
+        return;
+      }
+      multiPolygonModifications = getMultiPolygon({
+        x: modification.x,
+        y: modification.y,
+        width: modification.width,
+        height: modification.height,
+        shapes: shape.shapes,
+        pointToProjection: this.pointToProjection,
+        mirror: modification.direction && modification.direction === 'RIGHT',
+      });
+      this.polygons.push(...multiPolygonModifications);
+      multiPolygonModifications.forEach(polygon => {
+        const modificationStyle = new Style({
+          geometry: polygon,
+          stroke: getStroke({ color: rgbToHex(modification.borderColor) }),
+          fill: getFill({ color: rgbToHex(modification.fillColor) }),
+          zIndex: modification.z,
+        });
+        const modificationText = modification.stateAbbreviation
+          ? modification.stateAbbreviation
+          : modification.name;
+        if (modificationText) {
+          modificationStyle.setText(
+            new Text({
+              text: modificationText,
+              font: `${modification.fontSize}px Arial`,
+              textAlign: 'center',
+              textBaseline: 'middle',
+              fill: getFill({ color: '#000' }),
+              overflow: true,
+            }),
+          );
+          this.polygonsTexts.push(modification.name);
+        }
+        this.styles.push(modificationStyle);
+      });
+    });
+
+    if (this.lineType) {
+      const lineTypeFound = this.lineTypes.find(type => type.name === this.lineType);
+      if (lineTypeFound) {
+        this.lineDash = lineTypeFound.pattern;
+      }
+    }
+
+    const homodimerOffset = (this.homodimer - 1) * 6;
+    for (let i = 0; i < this.homodimer; i += 1) {
+      const homodimerShift = (this.homodimer - i - 1) * 6;
+      if (this.activity) {
+        this.drawActiveBorder(homodimerShift, homodimerOffset);
+      }
+      this.drawElementPolygon(homodimerShift, homodimerOffset);
+    }
+  }
+
+  drawActiveBorder(homodimerShift: number, homodimerOffset: number): void {
+    const activityBorderElement = getMultiPolygon({
+      x: this.x + homodimerShift - 5,
+      y: this.y + homodimerShift - 5,
+      width: this.width - homodimerOffset + 10,
+      height: this.height - homodimerOffset + 10,
+      shapes: this.shapes,
+      pointToProjection: this.pointToProjection,
+    });
+    activityBorderElement.forEach(polygon => {
+      this.styles.push(
+        getStyle({
+          geometry: polygon,
+          fillColor: { rgb: 0, alpha: 0 },
+          lineDash: [3, 5],
+          zIndex: this.zIndex,
+        }),
+      );
+    });
+    this.polygons.push(...activityBorderElement);
+  }
+
+  drawElementPolygon(homodimerShift: number, homodimerOffset: number): void {
+    const elementPolygon = getMultiPolygon({
+      x: this.x + homodimerShift,
+      y: this.y + homodimerShift,
+      width: this.width - homodimerOffset,
+      height: this.height - homodimerOffset,
+      shapes: this.shapes,
+      pointToProjection: this.pointToProjection,
+    });
+    elementPolygon.forEach(polygon => {
+      this.styles.push(
+        getStyle({
+          geometry: polygon,
+          borderColor: this.borderColor,
+          fillColor: this.fillColor,
+          lineWidth: this.lineWidth,
+          lineDash: this.lineDash,
+          zIndex: this.zIndex,
+        }),
+      );
+    });
+    this.polygons.push(...elementPolygon);
+  }
+}
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getMultiPolygon.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getMultiPolygon.test.ts
similarity index 96%
rename from src/components/Map/MapViewer/MapViewerVector/utils/shapes/getMultiPolygon.test.ts
rename to src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getMultiPolygon.test.ts
index 76036f498b7d92ab4040ed570e0fd6b7959582e4..dbe051bd31e7812e40e29baf8487387202fe57ff 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getMultiPolygon.test.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getMultiPolygon.test.ts
@@ -1,12 +1,12 @@
 /* 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 getPolygonCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getPolygonCoords';
+import getEllipseCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getEllipseCoords';
 import Polygon from 'ol/geom/Polygon';
 import { Shape } from '@/types/models';
 import getMultiPolygon from './getMultiPolygon';
 
-jest.mock('./getPolygonCoords');
-jest.mock('./getEllipseCoords');
+jest.mock('../coords/getPolygonCoords');
+jest.mock('../coords/getEllipseCoords');
 
 describe('getMultiPolygon', () => {
   const mockPointToProjection = jest.fn(point => [point.x, point.y]);
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getMultiPolygon.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getMultiPolygon.ts
similarity index 91%
rename from src/components/Map/MapViewer/MapViewerVector/utils/shapes/getMultiPolygon.ts
rename to src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getMultiPolygon.ts
index e5effdb72c71b538411248bf6223245bf5cb9c8b..d91c741abd1a4e477e3c5f6ed09a4fafad650b11 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getMultiPolygon.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getMultiPolygon.ts
@@ -1,11 +1,11 @@
 /* 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 getPolygonCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getPolygonCoords';
+import getEllipseCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getEllipseCoords';
 import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
 import Polygon from 'ol/geom/Polygon';
 import { Coordinate } from 'ol/coordinate';
 import { Shape } from '@/types/models';
-import getCentroid from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/getCentroid';
+import getCentroid from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getCentroid';
 
 export default function getMultiPolygon({
   x,
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getStroke.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getStroke.test.ts
deleted file mode 100644
index 70a27921c1a7510ddf9f4792000176aa962b41d6..0000000000000000000000000000000000000000
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getStroke.test.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-/* 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/getText.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getText.test.ts
deleted file mode 100644
index a9f0490707370aca9a3e9fe595aab4e83174bcff..0000000000000000000000000000000000000000
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getText.test.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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
deleted file mode 100644
index 10e021881d8e448edd9559a142d819d3b97de00c..0000000000000000000000000000000000000000
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getText.ts
+++ /dev/null
@@ -1,82 +0,0 @@
-/* eslint-disable no-magic-numbers */
-import { Fill, Text } from 'ol/style';
-import Style from 'ol/style/Style';
-import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
-import { Coordinate } from 'ol/coordinate';
-import {
-  HorizontalAlign,
-  VerticalAlign,
-} from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.types';
-import { FeatureLike } from 'ol/Feature';
-import { Geometry, MultiPolygon } from 'ol/geom';
-import RenderFeature from 'ol/render/Feature';
-
-export default function getText({
-  x,
-  y,
-  height,
-  width,
-  text = '',
-  fontSize = 12,
-  color = '#000',
-  zIndex = 1,
-  verticalAlign = 'MIDDLE',
-  horizontalAlign = 'CENTER',
-  pointToProjection,
-}: {
-  x: number;
-  y: number;
-  height: number;
-  width: number;
-  text: string;
-  fontSize?: string | number;
-  color?: string;
-  zIndex?: number;
-  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 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: new Text({
-      text,
-      font: `bold ${fontSize}px Arial`,
-      fill: new Fill({
-        color,
-      }),
-      placement: 'point',
-      textAlign: 'center',
-      textBaseline: 'middle',
-    }),
-    zIndex,
-  });
-
-  return { textCoords, textStyle };
-}
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/Layer.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/Layer.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ab6e4eb00b44740e62fdecc33d08a60d26b8e88e
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/Layer.test.ts
@@ -0,0 +1,145 @@
+/* eslint-disable no-magic-numbers */
+import { Map } from 'ol';
+import { Style, Text } from 'ol/style';
+import getTextStyle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextStyle';
+import getStroke from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStroke';
+import { rgbToHex } from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/rgbToHex';
+import View from 'ol/View';
+import {
+  WHITE_COLOR,
+  BLACK_COLOR,
+} from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants';
+import getTextCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextCoords';
+import Layer, {
+  LayerProps,
+} from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/Layer';
+import VectorSource from 'ol/source/Vector';
+import VectorLayer from 'ol/layer/Vector';
+
+jest.mock('../text/getTextCoords');
+jest.mock('../text/getTextStyle');
+jest.mock('../style/getStroke');
+jest.mock('../style/rgbToHex');
+
+describe('Layer', () => {
+  let props: LayerProps;
+
+  beforeEach(() => {
+    const dummyElement = document.createElement('div');
+    const mapInstance = new Map({
+      target: dummyElement,
+      view: new View({
+        zoom: 5,
+        minZoom: 3,
+        maxZoom: 7,
+      }),
+    });
+    props = {
+      texts: [
+        {
+          id: 1,
+          x: 10,
+          y: 10,
+          z: 3,
+          width: 100,
+          height: 100,
+          fontSize: 12,
+          size: 12312,
+          notes: 'XYZ',
+          glyph: null,
+          elementId: '34',
+          verticalAlign: 'MIDDLE',
+          horizontalAlign: 'CENTER',
+          backgroundColor: WHITE_COLOR,
+          borderColor: BLACK_COLOR,
+          color: BLACK_COLOR,
+        },
+      ],
+      rects: [
+        {
+          id: 1,
+          x: 10,
+          y: 10,
+          z: 3,
+          width: 100,
+          height: 100,
+          size: 12312,
+          elementId: '341',
+          lineWidth: 2,
+          borderColor: BLACK_COLOR,
+          fillColor: WHITE_COLOR,
+        },
+      ],
+      ovals: [
+        {
+          id: 1,
+          x: 10,
+          y: 10,
+          z: 3,
+          width: 100,
+          height: 100,
+          size: 12312,
+          elementId: '341',
+          lineWidth: 2,
+          borderColor: BLACK_COLOR,
+        },
+      ],
+      lines: [
+        {
+          id: 120899,
+          width: 5.0,
+          color: {
+            alpha: 255,
+            rgb: -16777216,
+          },
+          z: 0,
+          segments: [
+            {
+              x1: 36.0,
+              y1: 39.0,
+              x2: 213.0,
+              y2: 41.0,
+            },
+          ],
+          startArrow: {
+            arrowType: 'NONE',
+            angle: 2.748893571891069,
+            lineType: 'SOLID',
+            length: 15.0,
+          },
+          endArrow: {
+            arrowType: 'NONE',
+            angle: 2.748893571891069,
+            lineType: 'SOLID',
+            length: 15.0,
+          },
+          lineType: 'SOLID',
+        },
+      ],
+      visible: true,
+      layerId: '23',
+      pointToProjection: jest.fn(point => [point.x, point.y]),
+      mapInstance,
+      lineTypes: [],
+      arrowTypes: [],
+    };
+    (getTextStyle as jest.Mock).mockReturnValue(
+      new Style({
+        text: new Text({}),
+      }),
+    );
+    (getTextCoords as jest.Mock).mockReturnValue([10, 10]);
+    (getStroke as jest.Mock).mockReturnValue(new Style());
+    (rgbToHex as jest.Mock).mockReturnValue('#FFFFFF');
+  });
+
+  it('should initialize a Layer class', () => {
+    const layer = new Layer(props);
+
+    expect(layer.textFeatures.length).toBe(1);
+    expect(layer.rectFeatures.length).toBe(1);
+    expect(layer.ovalFeatures.length).toBe(1);
+    expect(layer.vectorSource).toBeInstanceOf(VectorSource);
+    expect(layer.vectorLayer).toBeInstanceOf(VectorLayer);
+  });
+});
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/Layer.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/Layer.ts
new file mode 100644
index 0000000000000000000000000000000000000000..259bff25fcd3512e1d5cbaf83f9aafa0f3448867
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/Layer.ts
@@ -0,0 +1,320 @@
+/* eslint-disable no-magic-numbers */
+import {
+  Arrow,
+  ArrowType,
+  LayerLine,
+  LayerOval,
+  LayerRect,
+  LayerText,
+  LineType,
+} from '@/types/models';
+import { MapInstance } from '@/types/map';
+import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
+import { Feature } from 'ol';
+import { LineString, MultiPolygon, Point } from 'ol/geom';
+import Text from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/Text';
+import Polygon from 'ol/geom/Polygon';
+import VectorSource from 'ol/source/Vector';
+import VectorLayer from 'ol/layer/Vector';
+import getEllipseCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getEllipseCoords';
+import getStyle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStyle';
+import {
+  HorizontalAlign,
+  VerticalAlign,
+} from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.types';
+import getMultiPolygon from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getMultiPolygon';
+import Style from 'ol/style/Style';
+import { BLACK_COLOR } from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants';
+import getRotation from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getRotation';
+
+export interface LayerProps {
+  texts: Array<LayerText>;
+  rects: Array<LayerRect>;
+  ovals: Array<LayerOval>;
+  lines: Array<LayerLine>;
+  visible: boolean;
+  layerId: string;
+  lineTypes: Array<LineType>;
+  arrowTypes: Array<ArrowType>;
+  mapInstance: MapInstance;
+  pointToProjection: UsePointToProjectionResult;
+}
+
+export default class Layer {
+  texts: Array<LayerText>;
+
+  rects: Array<LayerRect>;
+
+  ovals: Array<LayerOval>;
+
+  lines: Array<LayerLine>;
+
+  lineTypes: Array<LineType>;
+
+  arrowTypes: Array<ArrowType>;
+
+  textFeatures: Array<Feature<Point>>;
+
+  rectFeatures: Array<Feature<Polygon>>;
+
+  ovalFeatures: Array<Feature<Polygon>>;
+
+  lineFeatures: Array<Feature<LineString>>;
+
+  arrowFeatures: Array<Feature<MultiPolygon>>;
+
+  vectorSource: VectorSource<
+    Feature<Point> | Feature<Polygon> | Feature<LineString> | Feature<MultiPolygon>
+  >;
+
+  vectorLayer: VectorLayer<
+    VectorSource<Feature<Point> | Feature<Polygon> | Feature<LineString> | Feature<MultiPolygon>>
+  >;
+
+  constructor({
+    texts,
+    rects,
+    ovals,
+    lines,
+    visible,
+    layerId,
+    lineTypes,
+    arrowTypes,
+    mapInstance,
+    pointToProjection,
+  }: LayerProps) {
+    this.texts = texts;
+    this.rects = rects;
+    this.ovals = ovals;
+    this.lines = lines;
+    this.lineTypes = lineTypes;
+    this.arrowTypes = arrowTypes;
+    this.textFeatures = this.getTextsFeatures(mapInstance, pointToProjection);
+    this.rectFeatures = this.getRectsFeatures(pointToProjection);
+    this.ovalFeatures = this.getOvalsFeatures(pointToProjection);
+    const { linesFeatures, arrowsFeatures } = this.getLinesFeatures(pointToProjection);
+    this.lineFeatures = linesFeatures;
+    this.arrowFeatures = arrowsFeatures;
+    this.vectorSource = new VectorSource({
+      features: [
+        ...this.textFeatures,
+        ...this.rectFeatures,
+        ...this.ovalFeatures,
+        ...this.lineFeatures,
+        ...this.arrowFeatures,
+      ],
+    });
+    this.vectorLayer = new VectorLayer({
+      source: this.vectorSource,
+      visible,
+    });
+    this.vectorLayer.set('id', layerId);
+  }
+
+  private getTextsFeatures = (
+    mapInstance: MapInstance,
+    pointToProjection: UsePointToProjectionResult,
+  ): Array<Feature<Point>> => {
+    const textObjects = this.texts.map(text => {
+      return new Text({
+        x: text.x,
+        y: text.y,
+        zIndex: text.z,
+        width: text.width,
+        height: text.height,
+        fontColor: text.color,
+        fontSize: text.fontSize,
+        text: text.notes,
+        verticalAlign: text.verticalAlign as VerticalAlign,
+        horizontalAlign: text.horizontalAlign as HorizontalAlign,
+        pointToProjection,
+        mapInstance,
+      });
+    });
+    return textObjects.map(text => text.feature);
+  };
+
+  private getRectsFeatures = (
+    pointToProjection: UsePointToProjectionResult,
+  ): Array<Feature<Polygon>> => {
+    return this.rects.map(rect => {
+      const polygon = new Polygon([
+        [
+          pointToProjection({ x: rect.x, y: rect.y }),
+          pointToProjection({ x: rect.x + rect.width, y: rect.y }),
+          pointToProjection({ x: rect.x + rect.width, y: rect.y + rect.height }),
+          pointToProjection({ x: rect.x, y: rect.y + rect.height }),
+        ],
+      ]);
+      const polygonStyle = getStyle({
+        geometry: polygon,
+        borderColor: rect.borderColor,
+        fillColor: rect.fillColor,
+        lineWidth: rect.lineWidth,
+        zIndex: rect.z,
+      });
+      const rectFeature = new Feature<Polygon>({
+        geometry: polygon,
+      });
+      rectFeature.setStyle(polygonStyle);
+      return rectFeature;
+    });
+  };
+
+  private getOvalsFeatures = (
+    pointToProjection: UsePointToProjectionResult,
+  ): Array<Feature<Polygon>> => {
+    return this.ovals.map(oval => {
+      const coords = getEllipseCoords({
+        x: oval.x,
+        y: oval.y,
+        height: oval.height,
+        width: oval.width,
+        pointToProjection,
+        points: 36,
+      });
+      const polygon = new Polygon([coords]);
+      const polygonStyle = getStyle({
+        geometry: polygon,
+        borderColor: oval.borderColor,
+        fillColor: { rgb: 0, alpha: 0 },
+        lineWidth: oval.lineWidth,
+        zIndex: oval.z,
+      });
+      const ovalFeature = new Feature<Polygon>({
+        geometry: polygon,
+      });
+      ovalFeature.setStyle(polygonStyle);
+      return ovalFeature;
+    });
+  };
+
+  private getLinesFeatures = (
+    pointToProjection: UsePointToProjectionResult,
+  ): {
+    linesFeatures: Array<Feature<LineString>>;
+    arrowsFeatures: Array<Feature<MultiPolygon>>;
+  } => {
+    const linesFeatures: Array<Feature<LineString>> = [];
+    const arrowsFeatures: Array<Feature<MultiPolygon>> = [];
+
+    this.lines.forEach(line => {
+      const points = line.segments
+        .map((segment, index) => {
+          if (index === 0) {
+            return [
+              pointToProjection({ x: segment.x1, y: segment.y1 }),
+              pointToProjection({ x: segment.x2, y: segment.y2 }),
+            ];
+          }
+          return [pointToProjection({ x: segment.x2, y: segment.y2 })];
+        })
+        .flat();
+      const lineString = new LineString(points);
+
+      let lineDash;
+      const lineTypeFound = this.lineTypes.find(type => type.name === line.lineType);
+      if (lineTypeFound) {
+        lineDash = lineTypeFound.pattern;
+      }
+      const lineStyle = getStyle({
+        geometry: lineString,
+        borderColor: line.color,
+        lineWidth: line.width,
+        lineDash,
+        zIndex: line.z,
+      });
+      const lineFeature = new Feature<LineString>({
+        geometry: lineString,
+      });
+      lineFeature.setStyle(lineStyle);
+      linesFeatures.push(lineFeature);
+      arrowsFeatures.push(...this.getLineArrowsFeatures(line, pointToProjection));
+    });
+    return { linesFeatures, arrowsFeatures };
+  };
+
+  private getLineArrowsFeatures = (
+    line: LayerLine,
+    pointToProjection: UsePointToProjectionResult,
+  ): Array<Feature<MultiPolygon>> => {
+    const arrowsFeatures: Array<Feature<MultiPolygon>> = [];
+    const firstSegment = line.segments[0];
+    const startArrowRotation = getRotation(
+      [firstSegment.x1, firstSegment.y1],
+      [firstSegment.x2, firstSegment.y2],
+    );
+    const startArrowFeature = this.getLineArrowFeature(
+      line.startArrow,
+      firstSegment.x1,
+      firstSegment.y1,
+      line.z,
+      startArrowRotation,
+      line.width,
+      pointToProjection,
+    );
+    if (startArrowFeature) {
+      arrowsFeatures.push(startArrowFeature);
+    }
+
+    const lastSegment = line.segments[line.segments.length - 1];
+    const endArrowRotation = getRotation(
+      [lastSegment.x1, lastSegment.y1],
+      [lastSegment.x2, lastSegment.y2],
+    );
+    const endArrowFeature = this.getLineArrowFeature(
+      line.endArrow,
+      lastSegment.x2,
+      lastSegment.y2,
+      line.z,
+      endArrowRotation,
+      line.width,
+      pointToProjection,
+    );
+    if (endArrowFeature) {
+      arrowsFeatures.push(endArrowFeature);
+    }
+    return arrowsFeatures;
+  };
+
+  private getLineArrowFeature = (
+    arrow: Arrow,
+    x: number,
+    y: number,
+    zIndex: number,
+    rotation: number,
+    lineWidth: number,
+    pointToProjection: UsePointToProjectionResult,
+  ): undefined | Feature<MultiPolygon> => {
+    const arrowShapes = this.arrowTypes.find(arrowType => arrowType.arrowType === arrow.arrowType)
+      ?.shapes;
+    if (!arrowShapes) {
+      return undefined;
+    }
+    const arrowMultiPolygon = getMultiPolygon({
+      x,
+      y: y - arrow.length / 2,
+      width: arrow.length,
+      height: arrow.length,
+      shapes: arrowShapes,
+      pointToProjection,
+    });
+    const arrowStyles: Array<Style> = [];
+    arrowMultiPolygon.forEach(polygon => {
+      const style = getStyle({
+        geometry: polygon,
+        zIndex,
+        borderColor: BLACK_COLOR,
+        fillColor: BLACK_COLOR,
+        lineWidth,
+      });
+      arrowStyles.push(style);
+      polygon.rotate(rotation, pointToProjection({ x, y }));
+    });
+    const arrowFeature = new Feature({
+      geometry: new MultiPolygon(arrowMultiPolygon),
+    });
+    arrowFeature.setStyle(arrowStyles);
+    return arrowFeature;
+  };
+}
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getFill.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getFill.test.ts
similarity index 100%
rename from src/components/Map/MapViewer/MapViewerVector/utils/shapes/getFill.test.ts
rename to src/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getFill.test.ts
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getFill.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getFill.ts
similarity index 100%
rename from src/components/Map/MapViewer/MapViewerVector/utils/shapes/getFill.ts
rename to src/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getFill.ts
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStroke.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStroke.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9f485a73152e822c029bf5f79334050c2a49b9b9
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStroke.test.ts
@@ -0,0 +1,27 @@
+/* eslint-disable no-magic-numbers */
+import { Stroke } from 'ol/style';
+import getStroke from './getStroke';
+
+describe('getStroke', () => {
+  it('should return a Stroke object with the default color and width', () => {
+    const result = getStroke({});
+    expect(result).toBeInstanceOf(Stroke);
+    expect(result.getColor()).toEqual('#000');
+    expect(result.getWidth()).toEqual(1);
+    expect(result.getLineDash()).toEqual([]);
+    expect(result.getLineCap()).toEqual('butt');
+  });
+
+  it('should return a Stroke object with the provided values', () => {
+    const color = '#ff0000';
+    const width = 2;
+    const lineDash = [10, 5];
+    const lineCap = 'round';
+    const result = getStroke({ color, width, lineDash, lineCap });
+    expect(result).toBeInstanceOf(Stroke);
+    expect(result.getColor()).toEqual(color);
+    expect(result.getWidth()).toEqual(width);
+    expect(result.getLineDash()).toEqual(lineDash);
+    expect(result.getLineCap()).toEqual(lineCap);
+  });
+});
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getStroke.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStroke.ts
similarity index 64%
rename from src/components/Map/MapViewer/MapViewerVector/utils/shapes/getStroke.ts
rename to src/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStroke.ts
index 37816224714777d7917bc77067caa4354b75c8ee..5625aedbf1134a372b9d5864aff453bf0d104d60 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/getStroke.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStroke.ts
@@ -4,12 +4,18 @@ import { Stroke } from 'ol/style';
 export default function getStroke({
   color = '#000',
   width = 1,
+  lineDash = [],
+  lineCap = 'butt',
 }: {
   color?: string;
   width?: number;
+  lineDash?: Array<number>;
+  lineCap?: string;
 }): Stroke {
   return new Stroke({
     color,
     width,
+    lineDash,
+    lineCap: lineCap as CanvasLineCap,
   });
 }
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStyle.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStyle.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e46f9ef0c2cea4a7ba48e7f338a08918a5c9bb18
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStyle.test.ts
@@ -0,0 +1,52 @@
+/* eslint-disable no-magic-numbers */
+import getStyle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStyle';
+import Style from 'ol/style/Style';
+import Polygon from 'ol/geom/Polygon';
+
+describe('getStyle', () => {
+  it('should return a Style object with the default values', () => {
+    const result = getStyle({});
+    expect(result).toBeInstanceOf(Style);
+    expect(result.getGeometry()).toEqual(null);
+    expect(result.getStroke()?.getWidth()).toEqual(1);
+    expect(result.getStroke()?.getColor()).toEqual('#000000FF');
+    expect(result.getStroke()?.getLineDash()).toEqual([]);
+    expect(result.getFill()?.getColor()).toEqual('#FFFFFFFF');
+    expect(result.getZIndex()).toEqual(1);
+  });
+
+  it('should return a Style object with the provided color and width', () => {
+    const geometry = new Polygon([
+      [
+        [10, 10],
+        [10, 10],
+      ],
+    ]);
+    const borderColor = {
+      alpha: 255,
+      rgb: -16777216,
+    };
+    const fillColor = {
+      alpha: 255,
+      rgb: -5646081,
+    };
+    const lineWidth = 3;
+    const lineDash = [10, 5];
+    const zIndex = 2;
+    const result = getStyle({
+      geometry,
+      borderColor,
+      fillColor,
+      lineWidth,
+      lineDash,
+      zIndex,
+    });
+    expect(result).toBeInstanceOf(Style);
+    expect(result.getGeometry()).toEqual(geometry);
+    expect(result.getStroke()?.getWidth()).toEqual(lineWidth);
+    expect(result.getStroke()?.getColor()).toEqual('#000000FF');
+    expect(result.getStroke()?.getLineDash()).toEqual(lineDash);
+    expect(result.getFill()?.getColor()).toEqual('#A9D8FFFF');
+    expect(result.getZIndex()).toEqual(zIndex);
+  });
+});
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStyle.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStyle.ts
new file mode 100644
index 0000000000000000000000000000000000000000..62f1615786437795cdaa34e9e920d629fe67d53a
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStyle.ts
@@ -0,0 +1,38 @@
+/* eslint-disable no-magic-numbers */
+import Style from 'ol/style/Style';
+import { Geometry } from 'ol/geom';
+import getStroke from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStroke';
+import { rgbToHex } from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/rgbToHex';
+import {
+  BLACK_COLOR,
+  WHITE_COLOR,
+} from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants';
+import { ColorObject } from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.types';
+import getFill from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getFill';
+
+export default function getStyle({
+  geometry,
+  borderColor = BLACK_COLOR,
+  fillColor = WHITE_COLOR,
+  lineWidth = 1,
+  lineDash = [],
+  zIndex = 1,
+}: {
+  geometry?: Geometry;
+  borderColor?: ColorObject;
+  fillColor?: ColorObject;
+  lineWidth?: number;
+  lineDash?: Array<number>;
+  zIndex?: number;
+}): Style {
+  return new Style({
+    geometry,
+    stroke: getStroke({
+      color: rgbToHex(borderColor),
+      width: lineWidth,
+      lineDash,
+    }),
+    fill: getFill({ color: rgbToHex(fillColor) }),
+    zIndex,
+  });
+}
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/rgbToHex.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/style/rgbToHex.test.ts
similarity index 100%
rename from src/components/Map/MapViewer/MapViewerVector/utils/shapes/rgbToHex.test.ts
rename to src/components/Map/MapViewer/MapViewerVector/utils/shapes/style/rgbToHex.test.ts
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/rgbToHex.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/style/rgbToHex.ts
similarity index 100%
rename from src/components/Map/MapViewer/MapViewerVector/utils/shapes/rgbToHex.ts
rename to src/components/Map/MapViewer/MapViewerVector/utils/shapes/style/rgbToHex.ts
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/text/Text.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/text/Text.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..68b8d9f8f1a73b0d99e2251282512c5e211d4859
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/text/Text.test.ts
@@ -0,0 +1,73 @@
+/* eslint-disable no-magic-numbers */
+import { Map } from 'ol';
+import { Style } from 'ol/style';
+import Text, { TextProps } from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/Text';
+import getTextStyle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextStyle';
+import { rgbToHex } from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/rgbToHex';
+import View from 'ol/View';
+import { BLACK_COLOR } from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants';
+import getTextCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextCoords';
+
+jest.mock('./getTextCoords');
+jest.mock('./getTextStyle');
+jest.mock('../style/rgbToHex');
+
+describe('Text', () => {
+  let props: TextProps;
+
+  beforeEach(() => {
+    const dummyElement = document.createElement('div');
+    const mapInstance = new Map({
+      target: dummyElement,
+      view: new View({
+        zoom: 5,
+        minZoom: 3,
+        maxZoom: 7,
+      }),
+    });
+    props = {
+      x: 0,
+      y: 0,
+      width: 100,
+      height: 100,
+      zIndex: 1,
+      text: 'Test',
+      fontSize: 12,
+      fontColor: BLACK_COLOR,
+      verticalAlign: 'MIDDLE',
+      horizontalAlign: 'CENTER',
+      pointToProjection: jest.fn(),
+      mapInstance,
+    };
+
+    (getTextStyle as jest.Mock).mockReturnValue(new Style());
+    (getTextCoords as jest.Mock).mockReturnValue([10, 10]);
+    (rgbToHex as jest.Mock).mockReturnValue('#FFFFFF');
+  });
+
+  it('should apply correct styles to the feature', () => {
+    const text = new Text(props);
+    const { feature } = text;
+
+    const style = feature.getStyleFunction()?.call(text, feature, 1);
+
+    if (Array.isArray(style)) {
+      expect(style.length).toBeGreaterThan(0);
+    } else {
+      expect(style).toBeInstanceOf(Style);
+    }
+  });
+
+  it('should hide text when the scaled font size is too small', () => {
+    const text = new Text(props);
+    const { feature } = text;
+
+    const style = feature.getStyleFunction()?.call(text, feature, 20);
+
+    if (Array.isArray(style)) {
+      expect(style[0].getText()?.getText()).toBeUndefined();
+    } else {
+      expect(style?.getText()?.getText()).toBeUndefined();
+    }
+  });
+});
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/text/Text.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/text/Text.ts
new file mode 100644
index 0000000000000000000000000000000000000000..687cf2fc76e8607c47b4159b0e3f18a6d3202b47
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/text/Text.ts
@@ -0,0 +1,115 @@
+/* eslint-disable no-magic-numbers */
+import {
+  ColorObject,
+  HorizontalAlign,
+  VerticalAlign,
+} from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.types';
+import { rgbToHex } from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/rgbToHex';
+import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
+import Style from 'ol/style/Style';
+import { Point } from 'ol/geom';
+import { Feature } from 'ol';
+import { FeatureLike } from 'ol/Feature';
+import { MapInstance } from '@/types/map';
+import getTextCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextCoords';
+import getTextStyle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextStyle';
+
+export interface TextProps {
+  x: number;
+  y: number;
+  width: number;
+  height: number;
+  zIndex: number;
+  text: string;
+  fontSize: number;
+  fontColor: ColorObject;
+  verticalAlign: VerticalAlign;
+  horizontalAlign: HorizontalAlign;
+  pointToProjection: UsePointToProjectionResult;
+  mapInstance: MapInstance;
+}
+
+export default class Text {
+  text: string;
+
+  fontSize: number;
+
+  style: Style;
+
+  point: Point;
+
+  feature: Feature<Point>;
+
+  constructor({
+    x,
+    y,
+    width,
+    height,
+    zIndex,
+    text,
+    fontSize,
+    fontColor,
+    verticalAlign,
+    horizontalAlign,
+    pointToProjection,
+    mapInstance,
+  }: TextProps) {
+    this.text = text;
+    this.fontSize = fontSize;
+
+    const textCoords = getTextCoords({
+      x,
+      y,
+      height,
+      width,
+      fontSize,
+      verticalAlign,
+      horizontalAlign,
+      pointToProjection,
+    });
+    const textStyle = getTextStyle({
+      text,
+      fontSize,
+      color: rgbToHex(fontColor),
+      zIndex,
+      horizontalAlign,
+    });
+    this.point = new Point(textCoords);
+    this.style = textStyle;
+    this.style.setGeometry(this.point);
+
+    this.feature = new Feature({
+      geometry: this.point,
+      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.feature.setStyle(this.styleFunction.bind(this));
+  }
+
+  protected styleFunction(feature: FeatureLike, resolution: number): Style | Array<Style> | void {
+    const getTextScale = feature.get('getTextScale');
+    let textScale = 1;
+    if (getTextScale instanceof Function) {
+      textScale = getTextScale(resolution);
+    }
+
+    if (this.style.getText()) {
+      if (this.fontSize * textScale > 4) {
+        this.style.getText()?.setScale(textScale);
+        this.style.getText()?.setText(this.text);
+      } else {
+        this.style.getText()?.setText(undefined);
+      }
+    }
+    return this.style;
+  }
+}
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextCoords.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextCoords.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6121a92d201c129bd1787002439609afdaf38f39
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextCoords.test.ts
@@ -0,0 +1,20 @@
+/* eslint-disable no-magic-numbers */
+import getTextCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextCoords';
+import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
+
+describe('getTextCoords', () => {
+  it('should return a text coords', () => {
+    const mockPointToProjection: UsePointToProjectionResult = jest.fn(point => [point.x, point.y]);
+    const textCoords = getTextCoords({
+      x: 20,
+      y: 20,
+      height: 100,
+      width: 100,
+      fontSize: 12,
+      verticalAlign: 'MIDDLE',
+      horizontalAlign: 'CENTER',
+      pointToProjection: mockPointToProjection,
+    });
+    expect(textCoords).toEqual([70, 70]);
+  });
+});
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextCoords.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextCoords.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3dcbb8fa775a7eedc9d4615c565f7d3f6da8afad
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextCoords.ts
@@ -0,0 +1,48 @@
+/* eslint-disable no-magic-numbers */
+import {
+  HorizontalAlign,
+  VerticalAlign,
+} from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.types';
+import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
+import { Coordinate } from 'ol/coordinate';
+
+export default function getTextCoords({
+  x,
+  y,
+  height,
+  width,
+  fontSize,
+  verticalAlign,
+  horizontalAlign,
+  pointToProjection,
+}: {
+  x: number;
+  y: number;
+  height: number;
+  width: number;
+  fontSize: number;
+  verticalAlign: VerticalAlign;
+  horizontalAlign: HorizontalAlign;
+  pointToProjection: UsePointToProjectionResult;
+}): Coordinate {
+  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 * 4) / 6;
+  } else if (verticalAlign === 'BOTTOM') {
+    textY = maxY - (fontSize * 4) / 6;
+  }
+
+  let textX = (minX + maxX) / 2;
+  if (['LEFT', 'START'].includes(horizontalAlign)) {
+    textX = minX;
+  } else if (['RIGHT', 'END'].includes(horizontalAlign)) {
+    textX = maxX;
+  }
+
+  return pointToProjection({ x: textX, y: textY });
+}
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextStyle.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextStyle.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d16623b5d20e27252138ee74dac0f5096c6cfaf9
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextStyle.test.ts
@@ -0,0 +1,26 @@
+/* eslint-disable no-magic-numbers */
+import getTextStyle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextStyle';
+import Style from 'ol/style/Style';
+
+describe('getTextStyle', () => {
+  it('should return a text style object', () => {
+    const text = 'Text styl test';
+    const fontSize = 12;
+    const color = '#CCFFCC';
+    const zIndex = 122;
+    const horizontalAlign = 'CENTER';
+    const textStyle = getTextStyle({
+      text,
+      fontSize,
+      color,
+      zIndex,
+      horizontalAlign,
+    });
+    expect(textStyle).toBeInstanceOf(Style);
+    expect(textStyle.getText()?.getText()).toBe(text);
+    expect(textStyle.getText()?.getFont()).toBe('12pt Arial');
+    expect(textStyle.getText()?.getFill()?.getColor()).toBe(color);
+    expect(textStyle.getZIndex()).toBe(zIndex);
+    expect(textStyle.getText()?.getTextAlign()).toBe(horizontalAlign.toLowerCase());
+  });
+});
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextStyle.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextStyle.ts
new file mode 100644
index 0000000000000000000000000000000000000000..da9eed330814e1a0fb1083bf0ce3236da58d8c0b
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextStyle.ts
@@ -0,0 +1,32 @@
+import { Fill, Text } from 'ol/style';
+import Style from 'ol/style/Style';
+import { HorizontalAlign } from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.types';
+
+export default function getTextStyle({
+  text,
+  fontSize,
+  color,
+  zIndex,
+  horizontalAlign,
+}: {
+  text: string;
+  fontSize: number;
+  color: string;
+  zIndex: number;
+  horizontalAlign: HorizontalAlign;
+}): Style {
+  return new Style({
+    text: new Text({
+      text,
+      font: `${fontSize}pt Arial`,
+      fill: new Fill({
+        color,
+      }),
+      placement: 'point',
+      textAlign: horizontalAlign.toLowerCase() as CanvasTextAlign,
+      textBaseline: 'middle',
+      overflow: true,
+    }),
+    zIndex,
+  });
+}
diff --git a/src/models/arrowTypeSchema.ts b/src/models/arrowTypeSchema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..929f6b5a749677411933645508923d7ca3bc0a5f
--- /dev/null
+++ b/src/models/arrowTypeSchema.ts
@@ -0,0 +1,7 @@
+import { z } from 'zod';
+import { shapeSchema } from '@/models/shapeSchema';
+
+export const arrowTypeSchema = z.object({
+  arrowType: z.string(),
+  shapes: z.array(shapeSchema),
+});
diff --git a/src/models/fixtures/arrowTypesFixture.ts b/src/models/fixtures/arrowTypesFixture.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2d4523530d792f059ed3985838f7a59ed31e7cf2
--- /dev/null
+++ b/src/models/fixtures/arrowTypesFixture.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 { arrowTypeSchema } from '@/models/arrowTypeSchema';
+
+export const arrowTypesFixture = createFixture(z.array(arrowTypeSchema), {
+  seed: ZOD_SEED,
+  array: { min: 3, max: 3 },
+});
diff --git a/src/models/fixtures/shapesFixture.ts b/src/models/fixtures/bioShapesFixture.ts
similarity index 79%
rename from src/models/fixtures/shapesFixture.ts
rename to src/models/fixtures/bioShapesFixture.ts
index e1ce1e00f3772e84fe94c31173c12aa6da986202..23a5884a008a622137c7acc082edb61ced883bda 100644
--- a/src/models/fixtures/shapesFixture.ts
+++ b/src/models/fixtures/bioShapesFixture.ts
@@ -4,7 +4,7 @@ import { z } from 'zod';
 import { createFixture } from 'zod-fixture';
 import { bioShapeSchema } from '@/models/bioShapeSchema';
 
-export const shapesFixture = createFixture(z.array(bioShapeSchema), {
+export const bioShapesFixture = createFixture(z.array(bioShapeSchema), {
   seed: ZOD_SEED,
   array: { min: 3, max: 3 },
 });
diff --git a/src/models/fixtures/layerLinesFixture.ts b/src/models/fixtures/layerLinesFixture.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fc15d3c2d364031d896d8803f30cad37d997f8fd
--- /dev/null
+++ b/src/models/fixtures/layerLinesFixture.ts
@@ -0,0 +1,10 @@
+import { ZOD_SEED } from '@/constants';
+// eslint-disable-next-line import/no-extraneous-dependencies
+import { createFixture } from 'zod-fixture';
+import { pageableSchema } from '@/models/pageableSchema';
+import { layerLineSchema } from '@/models/layerLineSchema';
+
+export const layerLinesFixture = createFixture(pageableSchema(layerLineSchema), {
+  seed: ZOD_SEED,
+  array: { min: 3, max: 3 },
+});
diff --git a/src/models/fixtures/layerOvalsFixture.ts b/src/models/fixtures/layerOvalsFixture.ts
new file mode 100644
index 0000000000000000000000000000000000000000..50544273d9e93617b5814b56ad6892b29bbd68ff
--- /dev/null
+++ b/src/models/fixtures/layerOvalsFixture.ts
@@ -0,0 +1,10 @@
+import { ZOD_SEED } from '@/constants';
+// eslint-disable-next-line import/no-extraneous-dependencies
+import { createFixture } from 'zod-fixture';
+import { pageableSchema } from '@/models/pageableSchema';
+import { layerOvalSchema } from '@/models/layerOvalSchema';
+
+export const layerOvalsFixture = createFixture(pageableSchema(layerOvalSchema), {
+  seed: ZOD_SEED,
+  array: { min: 3, max: 3 },
+});
diff --git a/src/models/fixtures/layerRectsFixture.ts b/src/models/fixtures/layerRectsFixture.ts
new file mode 100644
index 0000000000000000000000000000000000000000..98469f8b3a0fe5886264b403c72ec85ff9e48b54
--- /dev/null
+++ b/src/models/fixtures/layerRectsFixture.ts
@@ -0,0 +1,10 @@
+import { ZOD_SEED } from '@/constants';
+// eslint-disable-next-line import/no-extraneous-dependencies
+import { createFixture } from 'zod-fixture';
+import { pageableSchema } from '@/models/pageableSchema';
+import { layerRectSchema } from '@/models/layerRectSchema';
+
+export const layerRectsFixture = createFixture(pageableSchema(layerRectSchema), {
+  seed: ZOD_SEED,
+  array: { min: 3, max: 3 },
+});
diff --git a/src/models/fixtures/layerTextsFixture.ts b/src/models/fixtures/layerTextsFixture.ts
new file mode 100644
index 0000000000000000000000000000000000000000..26e01467b6b15a6ade31cfd0093aa75fb66f4e8b
--- /dev/null
+++ b/src/models/fixtures/layerTextsFixture.ts
@@ -0,0 +1,10 @@
+import { ZOD_SEED } from '@/constants';
+// eslint-disable-next-line import/no-extraneous-dependencies
+import { createFixture } from 'zod-fixture';
+import { layerTextSchema } from '@/models/layerTextSchema';
+import { pageableSchema } from '@/models/pageableSchema';
+
+export const layerTextsFixture = createFixture(pageableSchema(layerTextSchema), {
+  seed: ZOD_SEED,
+  array: { min: 3, max: 3 },
+});
diff --git a/src/models/fixtures/layersFixture.ts b/src/models/fixtures/layersFixture.ts
new file mode 100644
index 0000000000000000000000000000000000000000..65a4841af311501d3a7a55e475f813ee00cc2b25
--- /dev/null
+++ b/src/models/fixtures/layersFixture.ts
@@ -0,0 +1,10 @@
+import { ZOD_SEED } from '@/constants';
+// eslint-disable-next-line import/no-extraneous-dependencies
+import { createFixture } from 'zod-fixture';
+import { layerSchema } from '@/models/layerSchema';
+import { pageableSchema } from '@/models/pageableSchema';
+
+export const layersFixture = createFixture(pageableSchema(layerSchema), {
+  seed: ZOD_SEED,
+  array: { min: 1, max: 1 },
+});
diff --git a/src/models/fixtures/lineTypesFixture.ts b/src/models/fixtures/lineTypesFixture.ts
new file mode 100644
index 0000000000000000000000000000000000000000..56833e16cf716570cb9009df2ad4bdd6cba0d8c3
--- /dev/null
+++ b/src/models/fixtures/lineTypesFixture.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 { lineTypeSchema } from '@/models/lineTypeSchema';
+
+export const lineTypesFixture = createFixture(z.array(lineTypeSchema), {
+  seed: ZOD_SEED,
+  array: { min: 3, max: 3 },
+});
diff --git a/src/models/fixtures/modelElementsFixture.ts b/src/models/fixtures/modelElementsFixture.ts
index 87c8fb2edbe3b7183b177da0cce8d1d302603c19..0c3df37b83e3924d499e3470556dc1402e4635bf 100644
--- a/src/models/fixtures/modelElementsFixture.ts
+++ b/src/models/fixtures/modelElementsFixture.ts
@@ -1,9 +1,10 @@
 import { ZOD_SEED } from '@/constants';
 // eslint-disable-next-line import/no-extraneous-dependencies
 import { createFixture } from 'zod-fixture';
-import { modelElementsSchema } from '@/models/modelElementsSchema';
+import { modelElementSchema } from '@/models/modelElementSchema';
+import { pageableSchema } from '@/models/pageableSchema';
 
-export const modelElementsFixture = createFixture(modelElementsSchema, {
+export const modelElementsFixture = createFixture(pageableSchema(modelElementSchema), {
   seed: ZOD_SEED,
   array: { min: 3, max: 3 },
 });
diff --git a/src/models/layerLineSchema.ts b/src/models/layerLineSchema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e454f9302cae6a7f0ccec85aa087a0bf8413aa43
--- /dev/null
+++ b/src/models/layerLineSchema.ts
@@ -0,0 +1,15 @@
+import { z } from 'zod';
+import { colorSchema } from '@/models/colorSchema';
+import { segmentSchema } from '@/models/segmentSchema';
+import { arrowSchema } from '@/models/arrowSchema';
+
+export const layerLineSchema = z.object({
+  id: z.number().int().positive(),
+  width: z.number(),
+  color: colorSchema,
+  z: z.number(),
+  segments: z.array(segmentSchema),
+  startArrow: arrowSchema,
+  endArrow: arrowSchema,
+  lineType: z.string(),
+});
diff --git a/src/models/layerOvalSchema.ts b/src/models/layerOvalSchema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..abd708efd3456e8d744d7182c5656a9199a66b8d
--- /dev/null
+++ b/src/models/layerOvalSchema.ts
@@ -0,0 +1,15 @@
+import { z } from 'zod';
+import { colorSchema } from '@/models/colorSchema';
+
+export const layerOvalSchema = z.object({
+  id: z.number().int().positive(),
+  x: z.number(),
+  y: z.number(),
+  z: z.number(),
+  width: z.number(),
+  height: z.number(),
+  lineWidth: z.number(),
+  size: z.number(),
+  elementId: z.string(),
+  borderColor: colorSchema,
+});
diff --git a/src/models/layerRectSchema.ts b/src/models/layerRectSchema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..440c96d4bcc43c8a55de0c8ba2f6b13b50443e09
--- /dev/null
+++ b/src/models/layerRectSchema.ts
@@ -0,0 +1,16 @@
+import { z } from 'zod';
+import { colorSchema } from '@/models/colorSchema';
+
+export const layerRectSchema = z.object({
+  id: z.number().int().positive(),
+  x: z.number(),
+  y: z.number(),
+  z: z.number(),
+  width: z.number(),
+  height: z.number(),
+  lineWidth: z.number(),
+  size: z.number(),
+  fillColor: colorSchema,
+  borderColor: colorSchema,
+  elementId: z.string(),
+});
diff --git a/src/models/layerSchema.ts b/src/models/layerSchema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..be2ba10b1a0fe09971903c64ff8128e564244366
--- /dev/null
+++ b/src/models/layerSchema.ts
@@ -0,0 +1,10 @@
+import { z } from 'zod';
+
+export const layerSchema = z.object({
+  id: z.number(),
+  layerId: z.string(),
+  name: z.string(),
+  visible: z.boolean(),
+  locked: z.boolean(),
+  empty: z.boolean(),
+});
diff --git a/src/models/layerTextSchema.ts b/src/models/layerTextSchema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3ad77ed0e59946a8a2a8586eb8ab8b237e67f618
--- /dev/null
+++ b/src/models/layerTextSchema.ts
@@ -0,0 +1,22 @@
+import { z } from 'zod';
+import { colorSchema } from '@/models/colorSchema';
+import { glyphSchema } from '@/models/glyphSchema';
+
+export const layerTextSchema = z.object({
+  id: z.number(),
+  x: z.number(),
+  y: z.number(),
+  z: z.number(),
+  width: z.number(),
+  height: z.number(),
+  fontSize: z.number(),
+  size: z.number(),
+  notes: z.string(),
+  glyph: glyphSchema.nullable(),
+  elementId: z.string(),
+  verticalAlign: z.string(),
+  horizontalAlign: z.string(),
+  backgroundColor: colorSchema,
+  borderColor: colorSchema,
+  color: colorSchema,
+});
diff --git a/src/models/lineTypeSchema.ts b/src/models/lineTypeSchema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3527033cc0a3aebe4f4459838bcf05c7258ecc2b
--- /dev/null
+++ b/src/models/lineTypeSchema.ts
@@ -0,0 +1,6 @@
+import { z } from 'zod';
+
+export const lineTypeSchema = z.object({
+  name: z.string(),
+  pattern: z.array(z.number()),
+});
diff --git a/src/models/modelElementSchema.ts b/src/models/modelElementSchema.ts
index 7b3291a1d3bbde11ad58e0b27a19df6d77b73959..98f2882560db3d36ae8b4a7e415b506e2e3e083f 100644
--- a/src/models/modelElementSchema.ts
+++ b/src/models/modelElementSchema.ts
@@ -37,16 +37,21 @@ export const modelElementSchema = z.object({
   formerSymbols: z.array(z.string()),
   activity: z.boolean().optional(),
   lineWidth: z.number().optional(),
+  innerWidth: z.number().optional(),
+  outerWidth: z.number().optional(),
+  thickness: z.number().optional(),
+  shape: z.enum(['SQUARE_COMPARTMENT', 'OVAL_COMPARTMENT', 'PATHWAY']).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(),
+  homodimer: z.number().optional(),
   hypothetical: z.boolean().nullable().optional(),
   boundaryCondition: z.boolean().nullable().optional(),
   constant: z.boolean().nullable().optional(),
   substanceUnits: z.boolean().nullable().optional(),
+  borderLineType: z.string().optional(),
   references: z.array(referenceSchema),
   sboTerm: z.string(),
   modificationResidues: z.array(modelElementModificationSchema).optional(),
diff --git a/src/models/modelElementsSchema.ts b/src/models/modelElementsSchema.ts
deleted file mode 100644
index 19969ba1c00f2155a545e9c1a45ea2ce867a4eea..0000000000000000000000000000000000000000
--- a/src/models/modelElementsSchema.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-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/redux/apiPath.ts b/src/redux/apiPath.ts
index 01fae40c7ce0e33a8e140d734d054c440aa3425e..c193f101d8e4be76a24fc3520f497b4cb9d9e3ec 100644
--- a/src/redux/apiPath.ts
+++ b/src/redux/apiPath.ts
@@ -51,6 +51,17 @@ export const apiPath = {
   getModelElements: (modelId: number): string =>
     `projects/${PROJECT_ID}/maps/${modelId}/bioEntities/elements/?size=10000`,
   getShapes: (): string => `projects/${PROJECT_ID}/shapes/`,
+  getLineTypes: (): string => `projects/${PROJECT_ID}/lineTypes/`,
+  getArrowTypes: (): string => `projects/${PROJECT_ID}/arrowTypes/`,
+  getLayers: (modelId: number): string => `projects/${PROJECT_ID}/maps/${modelId}/layers/`,
+  getLayerTexts: (modelId: number, layerId: number): string =>
+    `projects/${PROJECT_ID}/maps/${modelId}/layers/${layerId}/texts/`,
+  getLayerRects: (modelId: number, layerId: number): string =>
+    `projects/${PROJECT_ID}/maps/${modelId}/layers/${layerId}/rects/`,
+  getLayerOvals: (modelId: number, layerId: number): string =>
+    `projects/${PROJECT_ID}/maps/${modelId}/layers/${layerId}/ovals/`,
+  getLayerLines: (modelId: number, layerId: number): string =>
+    `projects/${PROJECT_ID}/maps/${modelId}/layers/${layerId}/lines/`,
   getChemicalsStringWithQuery: (searchQuery: string): string =>
     `projects/${PROJECT_ID}/chemicals:search?query=${searchQuery}`,
   getAllOverlaysByProjectIdQuery: (
diff --git a/src/redux/layers/layers.constants.ts b/src/redux/layers/layers.constants.ts
new file mode 100644
index 0000000000000000000000000000000000000000..56736b9f36f7a1d64e98587554a243b025247470
--- /dev/null
+++ b/src/redux/layers/layers.constants.ts
@@ -0,0 +1 @@
+export const LAYERS_FETCHING_ERROR_PREFIX = 'Failed to fetch layers';
diff --git a/src/redux/layers/layers.mock.ts b/src/redux/layers/layers.mock.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9ec2ce4c48696d6c1b75c008bfcc2f1624475ebd
--- /dev/null
+++ b/src/redux/layers/layers.mock.ts
@@ -0,0 +1,11 @@
+import { LayersState } from '@/redux/layers/layers.types';
+import { DEFAULT_ERROR } from '@/constants/errors';
+
+export const LAYERS_STATE_INITIAL_MOCK: LayersState = {
+  data: {
+    layers: [],
+    layersVisibility: {},
+  },
+  loading: 'idle',
+  error: DEFAULT_ERROR,
+};
diff --git a/src/redux/layers/layers.reducers.test.ts b/src/redux/layers/layers.reducers.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..827c1dff5f1b500b5d59fd70c2cf14581c4dec40
--- /dev/null
+++ b/src/redux/layers/layers.reducers.test.ts
@@ -0,0 +1,128 @@
+/* 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 { LAYERS_STATE_INITIAL_MOCK } from '@/redux/layers/layers.mock';
+import { getLayers } from '@/redux/layers/layers.thunks';
+import { layersFixture } from '@/models/fixtures/layersFixture';
+import { layerTextsFixture } from '@/models/fixtures/layerTextsFixture';
+import { layerRectsFixture } from '@/models/fixtures/layerRectsFixture';
+import { layerOvalsFixture } from '@/models/fixtures/layerOvalsFixture';
+import { layerLinesFixture } from '@/models/fixtures/layerLinesFixture';
+import { LayersState } from './layers.types';
+import layersReducer from './layers.slice';
+
+const mockedAxiosClient = mockNetworkNewAPIResponse();
+
+const INITIAL_STATE: LayersState = LAYERS_STATE_INITIAL_MOCK;
+
+describe('layers reducer', () => {
+  let store = {} as ToolkitStoreWithSingleSlice<LayersState>;
+  beforeEach(() => {
+    store = createStoreInstanceUsingSliceReducer('layers', layersReducer);
+  });
+
+  it('should match initial state', () => {
+    const action = { type: 'unknown' };
+
+    expect(layersReducer(undefined, action)).toEqual(INITIAL_STATE);
+  });
+
+  it('should update store after successful getLayers query', async () => {
+    mockedAxiosClient.onGet(apiPath.getLayers(1)).reply(HttpStatusCode.Ok, layersFixture);
+    mockedAxiosClient
+      .onGet(apiPath.getLayerTexts(1, layersFixture.content[0].id))
+      .reply(HttpStatusCode.Ok, layerTextsFixture);
+    mockedAxiosClient
+      .onGet(apiPath.getLayerRects(1, layersFixture.content[0].id))
+      .reply(HttpStatusCode.Ok, layerRectsFixture);
+    mockedAxiosClient
+      .onGet(apiPath.getLayerOvals(1, layersFixture.content[0].id))
+      .reply(HttpStatusCode.Ok, layerOvalsFixture);
+    mockedAxiosClient
+      .onGet(apiPath.getLayerLines(1, layersFixture.content[0].id))
+      .reply(HttpStatusCode.Ok, layerLinesFixture);
+
+    const { type } = await store.dispatch(getLayers(1));
+    const { data, loading, error } = store.getState().layers;
+    expect(type).toBe('vectorMap/getLayers/fulfilled');
+    expect(loading).toEqual('succeeded');
+    expect(error).toEqual({ message: '', name: '' });
+    expect(data).toEqual({
+      layers: [
+        {
+          details: layersFixture.content[0],
+          texts: layerTextsFixture.content,
+          rects: layerRectsFixture.content,
+          ovals: layerOvalsFixture.content,
+          lines: layerLinesFixture.content,
+        },
+      ],
+      layersVisibility: {
+        [layersFixture.content[0].layerId]: layersFixture.content[0].visible,
+      },
+    });
+  });
+
+  it('should update store after failed getLayers query', async () => {
+    mockedAxiosClient.onGet(apiPath.getLayers(1)).reply(HttpStatusCode.NotFound, []);
+
+    const action = await store.dispatch(getLayers(1));
+    const { data, loading, error } = store.getState().layers;
+
+    expect(action.type).toBe('vectorMap/getLayers/rejected');
+    expect(() => unwrapResult(action)).toThrow(
+      "Failed to fetch layers: 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({ layers: [], layersVisibility: {} });
+  });
+
+  it('should update store on loading getLayers query', async () => {
+    mockedAxiosClient.onGet(apiPath.getLayers(1)).reply(HttpStatusCode.Ok, layersFixture);
+    mockedAxiosClient
+      .onGet(apiPath.getLayerTexts(1, layersFixture.content[0].id))
+      .reply(HttpStatusCode.Ok, layerTextsFixture);
+    mockedAxiosClient
+      .onGet(apiPath.getLayerRects(1, layersFixture.content[0].id))
+      .reply(HttpStatusCode.Ok, layerRectsFixture);
+    mockedAxiosClient
+      .onGet(apiPath.getLayerOvals(1, layersFixture.content[0].id))
+      .reply(HttpStatusCode.Ok, layerOvalsFixture);
+    mockedAxiosClient
+      .onGet(apiPath.getLayerLines(1, layersFixture.content[0].id))
+      .reply(HttpStatusCode.Ok, layerLinesFixture);
+
+    const layersPromise = store.dispatch(getLayers(1));
+
+    const { data, loading } = store.getState().layers;
+    expect(data).toEqual({ layers: [], layersVisibility: {} });
+    expect(loading).toEqual('pending');
+
+    layersPromise.then(() => {
+      const { data: dataPromiseFulfilled, loading: promiseFulfilled } = store.getState().layers;
+
+      expect(dataPromiseFulfilled).toEqual({
+        layers: [
+          {
+            details: layersFixture.content[0],
+            texts: layerTextsFixture.content,
+            rects: layerRectsFixture.content,
+            ovals: layerOvalsFixture.content,
+            lines: layerLinesFixture.content,
+          },
+        ],
+        layersVisibility: {
+          [layersFixture.content[0].layerId]: layersFixture.content[0].visible,
+        },
+      });
+      expect(promiseFulfilled).toEqual('succeeded');
+    });
+  });
+});
diff --git a/src/redux/layers/layers.reducers.ts b/src/redux/layers/layers.reducers.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ed75a68735150b29e6a2fa96f61360853f5f7160
--- /dev/null
+++ b/src/redux/layers/layers.reducers.ts
@@ -0,0 +1,30 @@
+/* eslint-disable no-magic-numbers */
+import { ActionReducerMapBuilder, PayloadAction } from '@reduxjs/toolkit';
+import { getLayers } from '@/redux/layers/layers.thunks';
+import { LayersState } from '@/redux/layers/layers.types';
+
+export const getLayersReducer = (builder: ActionReducerMapBuilder<LayersState>): void => {
+  builder.addCase(getLayers.pending, state => {
+    state.loading = 'pending';
+  });
+  builder.addCase(getLayers.fulfilled, (state, action) => {
+    state.data = action.payload || {
+      layers: [],
+      layersVisibility: {},
+    };
+    state.loading = 'succeeded';
+  });
+  builder.addCase(getLayers.rejected, state => {
+    state.loading = 'failed';
+  });
+};
+
+export const setLayerVisibilityReducer = (
+  state: LayersState,
+  action: PayloadAction<{ visible: boolean; layerId: string }>,
+): void => {
+  const { payload } = action;
+  if (state.data && state.data.layersVisibility[payload.layerId] !== undefined) {
+    state.data.layersVisibility[payload.layerId] = payload.visible;
+  }
+};
diff --git a/src/redux/layers/layers.selectors.ts b/src/redux/layers/layers.selectors.ts
new file mode 100644
index 0000000000000000000000000000000000000000..987ec4ac2855d7e3c1a30857301034ef4dec7daf
--- /dev/null
+++ b/src/redux/layers/layers.selectors.ts
@@ -0,0 +1,12 @@
+import { createSelector } from '@reduxjs/toolkit';
+import { rootSelector } from '@/redux/root/root.selectors';
+
+export const layersSelector = createSelector(
+  rootSelector,
+  state => state.layers?.data?.layers || [],
+);
+
+export const layersVisibilitySelector = createSelector(
+  rootSelector,
+  state => state.layers?.data?.layersVisibility || {},
+);
diff --git a/src/redux/layers/layers.slice.ts b/src/redux/layers/layers.slice.ts
new file mode 100644
index 0000000000000000000000000000000000000000..47da06b01b1205dcdf422265bcf36e660f311fd3
--- /dev/null
+++ b/src/redux/layers/layers.slice.ts
@@ -0,0 +1,18 @@
+import { createSlice } from '@reduxjs/toolkit';
+import { LAYERS_STATE_INITIAL_MOCK } from '@/redux/layers/layers.mock';
+import { getLayersReducer, setLayerVisibilityReducer } from '@/redux/layers/layers.reducers';
+
+export const layersSlice = createSlice({
+  name: 'layers',
+  initialState: LAYERS_STATE_INITIAL_MOCK,
+  reducers: {
+    setLayerVisibility: setLayerVisibilityReducer,
+  },
+  extraReducers: builder => {
+    getLayersReducer(builder);
+  },
+});
+
+export const { setLayerVisibility } = layersSlice.actions;
+
+export default layersSlice.reducer;
diff --git a/src/redux/layers/layers.thunks.test.ts b/src/redux/layers/layers.thunks.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a6c0018672be6bcdad62f83e1464ea8f3fbe2d60
--- /dev/null
+++ b/src/redux/layers/layers.thunks.test.ts
@@ -0,0 +1,68 @@
+/* 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 { LayersState } from '@/redux/layers/layers.types';
+import { getLayers } from '@/redux/layers/layers.thunks';
+import { layersFixture } from '@/models/fixtures/layersFixture';
+import { layerTextsFixture } from '@/models/fixtures/layerTextsFixture';
+import { layerRectsFixture } from '@/models/fixtures/layerRectsFixture';
+import { layerOvalsFixture } from '@/models/fixtures/layerOvalsFixture';
+import { layerLinesFixture } from '@/models/fixtures/layerLinesFixture';
+import layersReducer from './layers.slice';
+
+const mockedAxiosClient = mockNetworkNewAPIResponse();
+
+describe('layers thunks', () => {
+  let store = {} as ToolkitStoreWithSingleSlice<LayersState>;
+  beforeEach(() => {
+    store = createStoreInstanceUsingSliceReducer('layers', layersReducer);
+  });
+
+  describe('getLayers', () => {
+    it('should return data when data response from API is valid', async () => {
+      mockedAxiosClient.onGet(apiPath.getLayers(1)).reply(HttpStatusCode.Ok, layersFixture);
+      mockedAxiosClient
+        .onGet(apiPath.getLayerTexts(1, layersFixture.content[0].id))
+        .reply(HttpStatusCode.Ok, layerTextsFixture);
+      mockedAxiosClient
+        .onGet(apiPath.getLayerRects(1, layersFixture.content[0].id))
+        .reply(HttpStatusCode.Ok, layerRectsFixture);
+      mockedAxiosClient
+        .onGet(apiPath.getLayerOvals(1, layersFixture.content[0].id))
+        .reply(HttpStatusCode.Ok, layerOvalsFixture);
+      mockedAxiosClient
+        .onGet(apiPath.getLayerLines(1, layersFixture.content[0].id))
+        .reply(HttpStatusCode.Ok, layerLinesFixture);
+
+      const { payload } = await store.dispatch(getLayers(1));
+      expect(payload).toEqual({
+        layers: [
+          {
+            details: layersFixture.content[0],
+            texts: layerTextsFixture.content,
+            rects: layerRectsFixture.content,
+            ovals: layerOvalsFixture.content,
+            lines: layerLinesFixture.content,
+          },
+        ],
+        layersVisibility: {
+          [layersFixture.content[0].layerId]: layersFixture.content[0].visible,
+        },
+      });
+    });
+
+    it('should return undefined when data response from API is not valid ', async () => {
+      mockedAxiosClient
+        .onGet(apiPath.getLayers(1))
+        .reply(HttpStatusCode.Ok, { randomProperty: 'randomValue' });
+
+      const { payload } = await store.dispatch(getLayers(1));
+      expect(payload).toEqual(undefined);
+    });
+  });
+});
diff --git a/src/redux/layers/layers.thunks.ts b/src/redux/layers/layers.thunks.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9aa71e8301dcc714a7e52922d71656275e412887
--- /dev/null
+++ b/src/redux/layers/layers.thunks.ts
@@ -0,0 +1,65 @@
+import { z } from 'zod';
+import { apiPath } from '@/redux/apiPath';
+import { Layer, Layers } from '@/types/models';
+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 { layerSchema } from '@/models/layerSchema';
+import { LAYERS_FETCHING_ERROR_PREFIX } from '@/redux/layers/layers.constants';
+import { LayersVisibilitiesState } from '@/redux/layers/layers.types';
+import { layerTextSchema } from '@/models/layerTextSchema';
+import { layerRectSchema } from '@/models/layerRectSchema';
+import { pageableSchema } from '@/models/pageableSchema';
+import { layerOvalSchema } from '@/models/layerOvalSchema';
+import { layerLineSchema } from '@/models/layerLineSchema';
+
+export const getLayers = createAsyncThunk<LayersVisibilitiesState | undefined, number, ThunkConfig>(
+  'vectorMap/getLayers',
+  async (modelId: number) => {
+    try {
+      const { data } = await axiosInstanceNewAPI.get<Layers>(apiPath.getLayers(modelId));
+      const isDataValid = validateDataUsingZodSchema(data, pageableSchema(layerSchema));
+      if (!isDataValid) {
+        return undefined;
+      }
+      let layers = await Promise.all(
+        data.content.map(async (layer: Layer) => {
+          const [textsResponse, rectsResponse, ovalsResponse, linesResponse] = await Promise.all([
+            axiosInstanceNewAPI.get(apiPath.getLayerTexts(modelId, layer.id)),
+            axiosInstanceNewAPI.get(apiPath.getLayerRects(modelId, layer.id)),
+            axiosInstanceNewAPI.get(apiPath.getLayerOvals(modelId, layer.id)),
+            axiosInstanceNewAPI.get(apiPath.getLayerLines(modelId, layer.id)),
+          ]);
+
+          return {
+            details: layer,
+            texts: textsResponse.data.content,
+            rects: rectsResponse.data.content,
+            ovals: ovalsResponse.data.content,
+            lines: linesResponse.data.content,
+          };
+        }),
+      );
+      layers = layers.filter(layer => {
+        return (
+          z.array(layerTextSchema).safeParse(layer.texts).success &&
+          z.array(layerRectSchema).safeParse(layer.rects).success &&
+          z.array(layerOvalSchema).safeParse(layer.ovals).success &&
+          z.array(layerLineSchema).safeParse(layer.lines).success
+        );
+      });
+      const layersVisibility = layers.reduce((acc: { [key: string]: boolean }, layer) => {
+        acc[layer.details.layerId] = layer.details.visible;
+        return acc;
+      }, {});
+      return {
+        layers,
+        layersVisibility,
+      };
+    } catch (error) {
+      return Promise.reject(getError({ error, prefix: LAYERS_FETCHING_ERROR_PREFIX }));
+    }
+  },
+);
diff --git a/src/redux/layers/layers.types.ts b/src/redux/layers/layers.types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..636376906b991185f851a8ec1b1550b7dcdab440
--- /dev/null
+++ b/src/redux/layers/layers.types.ts
@@ -0,0 +1,21 @@
+import { FetchDataState } from '@/types/fetchDataState';
+import { Layer, LayerLine, LayerOval, LayerRect, LayerText } from '@/types/models';
+
+export type LayerState = {
+  details: Layer;
+  texts: LayerText[];
+  rects: LayerRect[];
+  ovals: LayerOval[];
+  lines: LayerLine[];
+};
+
+export type LayerVisibilityState = {
+  [key: string]: boolean;
+};
+
+export type LayersVisibilitiesState = {
+  layersVisibility: LayerVisibilityState;
+  layers: LayerState[];
+};
+
+export type LayersState = FetchDataState<LayersVisibilitiesState>;
diff --git a/src/redux/modelElements/modelElements.thunks.ts b/src/redux/modelElements/modelElements.thunks.ts
index 7898db9a04b263622c80cd3192c7ec4417935892..2c7e2f25a2b850d845d923c99e191a1f29b60b1c 100644
--- a/src/redux/modelElements/modelElements.thunks.ts
+++ b/src/redux/modelElements/modelElements.thunks.ts
@@ -4,9 +4,10 @@ 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';
+import { modelElementSchema } from '@/models/modelElementSchema';
+import { pageableSchema } from '@/models/pageableSchema';
 
 export const getModelElements = createAsyncThunk<ModelElements | undefined, number, ThunkConfig>(
   'vectorMap/getModelElements',
@@ -15,7 +16,10 @@ export const getModelElements = createAsyncThunk<ModelElements | undefined, numb
       const response = await axiosInstanceNewAPI.get<ModelElements>(
         apiPath.getModelElements(modelId),
       );
-      const isDataValid = validateDataUsingZodSchema(response.data, modelElementsSchema);
+      const isDataValid = validateDataUsingZodSchema(
+        response.data,
+        pageableSchema(modelElementSchema),
+      );
       return isDataValid ? response.data : undefined;
     } catch (error) {
       return Promise.reject(getError({ error, prefix: MODEL_ELEMENTS_FETCHING_ERROR_PREFIX }));
diff --git a/src/redux/root/init.thunks.ts b/src/redux/root/init.thunks.ts
index 4e161b6e786697a53f445b4b880fcafab26c3b7b..34f7765ecc3687074d2e1bc31c9b364fdb757e5d 100644
--- a/src/redux/root/init.thunks.ts
+++ b/src/redux/root/init.thunks.ts
@@ -15,7 +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 { getArrowTypes, getLineTypes, getShapes } from '@/redux/shapes/shapes.thunks';
 import { getAllBackgroundsByProjectId } from '../backgrounds/backgrounds.thunks';
 import { getConfiguration, getConfigurationOptions } from '../configuration/configuration.thunks';
 import {
@@ -60,8 +60,9 @@ export const fetchInitialAppData = createAsyncThunk<
     dispatch(getAllPublicOverlaysByProjectId(PROJECT_ID)),
     dispatch(getModels()),
     dispatch(getShapes()),
+    dispatch(getLineTypes()),
+    dispatch(getArrowTypes()),
   ]);
-
   if (queryData.pluginsId) {
     await dispatch(
       getInitPlugins({
diff --git a/src/redux/root/root.fixtures.ts b/src/redux/root/root.fixtures.ts
index 7800ab1931b4009204cc14282bbe5fdaee7e8bf0..8b1cec49d6e6fe6104c2487e898f080f322e2a1a 100644
--- a/src/redux/root/root.fixtures.ts
+++ b/src/redux/root/root.fixtures.ts
@@ -3,8 +3,9 @@ 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 { SHAPES_STATE_INITIAL_MOCK } from '@/redux/shapes/shapes.mock';
 import { MODEL_ELEMENTS_INITIAL_STATE_MOCK } from '@/redux/modelElements/modelElements.mock';
+import { LAYERS_STATE_INITIAL_MOCK } from '@/redux/layers/layers.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';
@@ -38,12 +39,13 @@ 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,
+  shapes: 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,
+  layers: LAYERS_STATE_INITIAL_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
index 04d8b1caed90ca32d964ad2a87e3de34c30e8938..18da4b1b0575dbe776b1c3b8e5e660a2b70e1ce1 100644
--- a/src/redux/shapes/shapes.constants.ts
+++ b/src/redux/shapes/shapes.constants.ts
@@ -1 +1,5 @@
 export const SHAPES_FETCHING_ERROR_PREFIX = 'Failed to fetch shapes';
+
+export const LINE_TYPES_FETCHING_ERROR_PREFIX = 'Failed to fetch line types';
+
+export const ARROW_TYPES_FETCHING_ERROR_PREFIX = 'Failed to fetch arrow types';
diff --git a/src/redux/shapes/shapes.mock.ts b/src/redux/shapes/shapes.mock.ts
index d9ee5018d64fe273b524f309299c1c8765666874..b1ad3a5eaa627abffc1793bbb41735afa9164f6a 100644
--- a/src/redux/shapes/shapes.mock.ts
+++ b/src/redux/shapes/shapes.mock.ts
@@ -1,8 +1,20 @@
 import { DEFAULT_ERROR } from '@/constants/errors';
-import { BioShapesState } from '@/redux/shapes/shapes.types';
+import { ShapesState } from '@/redux/shapes/shapes.types';
 
-export const BIO_SHAPES_STATE_INITIAL_MOCK: BioShapesState = {
-  data: [],
-  loading: 'idle',
-  error: DEFAULT_ERROR,
+export const SHAPES_STATE_INITIAL_MOCK: ShapesState = {
+  bioShapesState: {
+    data: [],
+    loading: 'idle',
+    error: DEFAULT_ERROR,
+  },
+  lineTypesState: {
+    data: [],
+    loading: 'idle',
+    error: DEFAULT_ERROR,
+  },
+  arrowTypesState: {
+    data: [],
+    loading: 'idle',
+    error: DEFAULT_ERROR,
+  },
 };
diff --git a/src/redux/shapes/shapes.reducers.test.ts b/src/redux/shapes/shapes.reducers.test.ts
index 41b8df566da00a148a623d3627e75f618a2c9512..9eab2b219d230f81f163b8464c5813f93a503005 100644
--- a/src/redux/shapes/shapes.reducers.test.ts
+++ b/src/redux/shapes/shapes.reducers.test.ts
@@ -6,21 +6,20 @@ import {
 import { mockNetworkNewAPIResponse } from '@/utils/mockNetworkResponse';
 import { HttpStatusCode } from 'axios';
 import { unwrapResult } from '@reduxjs/toolkit';
-import { shapesFixture } from '@/models/fixtures/shapesFixture';
+import { bioShapesFixture } from '@/models/fixtures/bioShapesFixture';
+import { SHAPES_STATE_INITIAL_MOCK } from '@/redux/shapes/shapes.mock';
+import { lineTypesFixture } from '@/models/fixtures/lineTypesFixture';
+import { arrowTypesFixture } from '@/models/fixtures/arrowTypesFixture';
 import shapesReducer from './shapes.slice';
-import { getShapes } from './shapes.thunks';
-import { BioShapesState } from './shapes.types';
+import { getArrowTypes, getLineTypes, getShapes } from './shapes.thunks';
+import { ShapesState } from './shapes.types';
 
 const mockedAxiosClient = mockNetworkNewAPIResponse();
 
-const INITIAL_STATE: BioShapesState = {
-  data: [],
-  loading: 'idle',
-  error: { name: '', message: '' },
-};
+const INITIAL_STATE: ShapesState = SHAPES_STATE_INITIAL_MOCK;
 
 describe('shapes reducer', () => {
-  let store = {} as ToolkitStoreWithSingleSlice<BioShapesState>;
+  let store = {} as ToolkitStoreWithSingleSlice<ShapesState>;
   beforeEach(() => {
     store = createStoreInstanceUsingSliceReducer('shapes', shapesReducer);
   });
@@ -31,22 +30,44 @@ describe('shapes reducer', () => {
     expect(shapesReducer(undefined, action)).toEqual(INITIAL_STATE);
   });
 
-  it('should update store after succesfull getShapes query', async () => {
-    mockedAxiosClient.onGet(apiPath.getShapes()).reply(HttpStatusCode.Ok, shapesFixture);
+  it('should update store after successful getShapes query', async () => {
+    mockedAxiosClient.onGet(apiPath.getShapes()).reply(HttpStatusCode.Ok, bioShapesFixture);
 
     const { type } = await store.dispatch(getShapes());
-    const { data, loading, error } = store.getState().shapes;
+    const { data, loading, error } = store.getState().shapes.bioShapesState;
     expect(type).toBe('vectorMap/getShapes/fulfilled');
     expect(loading).toEqual('succeeded');
     expect(error).toEqual({ message: '', name: '' });
-    expect(data).toEqual(shapesFixture);
+    expect(data).toEqual(bioShapesFixture);
+  });
+
+  it('should update store after successful getLineTypes query', async () => {
+    mockedAxiosClient.onGet(apiPath.getLineTypes()).reply(HttpStatusCode.Ok, lineTypesFixture);
+
+    const { type } = await store.dispatch(getLineTypes());
+    const { data, loading, error } = store.getState().shapes.lineTypesState;
+    expect(type).toBe('vectorMap/getLineTypes/fulfilled');
+    expect(loading).toEqual('succeeded');
+    expect(error).toEqual({ message: '', name: '' });
+    expect(data).toEqual(lineTypesFixture);
+  });
+
+  it('should update store after successful getArrowTypes query', async () => {
+    mockedAxiosClient.onGet(apiPath.getArrowTypes()).reply(HttpStatusCode.Ok, arrowTypesFixture);
+
+    const { type } = await store.dispatch(getArrowTypes());
+    const { data, loading, error } = store.getState().shapes.arrowTypesState;
+    expect(type).toBe('vectorMap/getArrowTypes/fulfilled');
+    expect(loading).toEqual('succeeded');
+    expect(error).toEqual({ message: '', name: '' });
+    expect(data).toEqual(arrowTypesFixture);
   });
 
   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;
+    const { data, loading, error } = store.getState().shapes.bioShapesState;
 
     expect(action.type).toBe('vectorMap/getShapes/rejected');
     expect(() => unwrapResult(action)).toThrow(
@@ -57,19 +78,86 @@ describe('shapes reducer', () => {
     expect(data).toEqual([]);
   });
 
+  it('should update store after failed getLineTypes query', async () => {
+    mockedAxiosClient.onGet(apiPath.getLineTypes()).reply(HttpStatusCode.NotFound, []);
+
+    const action = await store.dispatch(getLineTypes());
+    const { data, loading, error } = store.getState().shapes.lineTypesState;
+
+    expect(action.type).toBe('vectorMap/getLineTypes/rejected');
+    expect(() => unwrapResult(action)).toThrow(
+      "Failed to fetch line types: 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 after failed getArrowTypes query', async () => {
+    mockedAxiosClient.onGet(apiPath.getArrowTypes()).reply(HttpStatusCode.NotFound, []);
+
+    const action = await store.dispatch(getArrowTypes());
+    const { data, loading, error } = store.getState().shapes.arrowTypesState;
+
+    expect(action.type).toBe('vectorMap/getArrowTypes/rejected');
+    expect(() => unwrapResult(action)).toThrow(
+      "Failed to fetch arrow types: 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);
+    mockedAxiosClient.onGet(apiPath.getShapes()).reply(HttpStatusCode.Ok, bioShapesFixture);
 
     const shapesPromise = store.dispatch(getShapes());
 
-    const { data, loading } = store.getState().shapes;
+    const { data, loading } = store.getState().shapes.bioShapesState;
     expect(data).toEqual([]);
     expect(loading).toEqual('pending');
 
     shapesPromise.then(() => {
-      const { data: dataPromiseFulfilled, loading: promiseFulfilled } = store.getState().shapes;
+      const { data: dataPromiseFulfilled, loading: promiseFulfilled } =
+        store.getState().shapes.bioShapesState;
+
+      expect(dataPromiseFulfilled).toEqual(bioShapesFixture);
+      expect(promiseFulfilled).toEqual('succeeded');
+    });
+  });
+
+  it('should update store on loading getLineTypes query', async () => {
+    mockedAxiosClient.onGet(apiPath.getLineTypes()).reply(HttpStatusCode.Ok, lineTypesFixture);
+
+    const lineTypesPromise = store.dispatch(getLineTypes());
+
+    const { data, loading } = store.getState().shapes.lineTypesState;
+    expect(data).toEqual([]);
+    expect(loading).toEqual('pending');
+
+    lineTypesPromise.then(() => {
+      const { data: dataPromiseFulfilled, loading: promiseFulfilled } =
+        store.getState().shapes.lineTypesState;
+
+      expect(dataPromiseFulfilled).toEqual(lineTypesFixture);
+      expect(promiseFulfilled).toEqual('succeeded');
+    });
+  });
+
+  it('should update store on loading getArrowTypes query', async () => {
+    mockedAxiosClient.onGet(apiPath.getArrowTypes()).reply(HttpStatusCode.Ok, arrowTypesFixture);
+
+    const arrowTypesPromise = store.dispatch(getArrowTypes());
+
+    const { data, loading } = store.getState().shapes.arrowTypesState;
+    expect(data).toEqual([]);
+    expect(loading).toEqual('pending');
+
+    arrowTypesPromise.then(() => {
+      const { data: dataPromiseFulfilled, loading: promiseFulfilled } =
+        store.getState().shapes.arrowTypesState;
 
-      expect(dataPromiseFulfilled).toEqual(shapesFixture);
+      expect(dataPromiseFulfilled).toEqual(arrowTypesFixture);
       expect(promiseFulfilled).toEqual('succeeded');
     });
   });
diff --git a/src/redux/shapes/shapes.reducers.ts b/src/redux/shapes/shapes.reducers.ts
index 72ea84f0fba44c9e14aa8b0076496be48cd6fe5f..525cde54ca6b47cf1e06cf7af8f3b623e8c4c8c0 100644
--- a/src/redux/shapes/shapes.reducers.ts
+++ b/src/redux/shapes/shapes.reducers.ts
@@ -1,16 +1,42 @@
 import { ActionReducerMapBuilder } from '@reduxjs/toolkit';
-import { BioShapesState } from '@/redux/shapes/shapes.types';
-import { getShapes } from '@/redux/shapes/shapes.thunks';
+import { ShapesState } from '@/redux/shapes/shapes.types';
+import { getArrowTypes, getLineTypes, getShapes } from '@/redux/shapes/shapes.thunks';
 
-export const getShapesReducer = (builder: ActionReducerMapBuilder<BioShapesState>): void => {
+export const getShapesReducer = (builder: ActionReducerMapBuilder<ShapesState>): void => {
   builder.addCase(getShapes.pending, state => {
-    state.loading = 'pending';
+    state.bioShapesState.loading = 'pending';
   });
   builder.addCase(getShapes.fulfilled, (state, action) => {
-    state.data = action.payload || [];
-    state.loading = 'succeeded';
+    state.bioShapesState.data = action.payload || [];
+    state.bioShapesState.loading = 'succeeded';
   });
   builder.addCase(getShapes.rejected, state => {
-    state.loading = 'failed';
+    state.bioShapesState.loading = 'failed';
+  });
+};
+
+export const getLineTypesReducer = (builder: ActionReducerMapBuilder<ShapesState>): void => {
+  builder.addCase(getLineTypes.pending, state => {
+    state.lineTypesState.loading = 'pending';
+  });
+  builder.addCase(getLineTypes.fulfilled, (state, action) => {
+    state.lineTypesState.data = action.payload || [];
+    state.lineTypesState.loading = 'succeeded';
+  });
+  builder.addCase(getLineTypes.rejected, state => {
+    state.lineTypesState.loading = 'failed';
+  });
+};
+
+export const getArrowTypesReducer = (builder: ActionReducerMapBuilder<ShapesState>): void => {
+  builder.addCase(getArrowTypes.pending, state => {
+    state.arrowTypesState.loading = 'pending';
+  });
+  builder.addCase(getArrowTypes.fulfilled, (state, action) => {
+    state.arrowTypesState.data = action.payload || [];
+    state.arrowTypesState.loading = 'succeeded';
+  });
+  builder.addCase(getArrowTypes.rejected, state => {
+    state.arrowTypesState.loading = 'failed';
   });
 };
diff --git a/src/redux/shapes/shapes.selectors.ts b/src/redux/shapes/shapes.selectors.ts
index 6faab0fe791516a689f72b1b8be01d12298b8220..35569752e5b3dbd9a26ccac4b22210dff147b7f6 100644
--- a/src/redux/shapes/shapes.selectors.ts
+++ b/src/redux/shapes/shapes.selectors.ts
@@ -3,7 +3,23 @@ import { rootSelector } from '@/redux/root/root.selectors';
 
 export const shapesSelector = createSelector(rootSelector, state => state.shapes);
 
+export const bioShapesSelector = createSelector(
+  shapesSelector,
+  shapes => shapes.bioShapesState.data,
+);
+
+export const lineTypesSelector = createSelector(
+  shapesSelector,
+  shapes => shapes.lineTypesState.data,
+);
+
+export const arrowTypesSelector = createSelector(
+  shapesSelector,
+  shapes => shapes.arrowTypesState.data,
+);
+
 export const shapeBySBOSelector = createSelector(
   [shapesSelector, (_state, shapeSBO: string): string => shapeSBO],
-  (shapes, shapeSBO) => (shapes?.data || []).find(({ sboTerm }) => sboTerm === shapeSBO),
+  (shapes, shapeSBO) =>
+    (shapes?.bioShapesState.data || []).find(({ sboTerm }) => sboTerm === shapeSBO),
 );
diff --git a/src/redux/shapes/shapes.slice.ts b/src/redux/shapes/shapes.slice.ts
index 06c08ec04e1a92e70d963c584b47154e013b15a3..7fcecfa0678aac6cb76db024c05cbe0ced0ca638 100644
--- a/src/redux/shapes/shapes.slice.ts
+++ b/src/redux/shapes/shapes.slice.ts
@@ -1,19 +1,19 @@
-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: '' },
-};
+import {
+  getArrowTypesReducer,
+  getLineTypesReducer,
+  getShapesReducer,
+} from '@/redux/shapes/shapes.reducers';
+import { SHAPES_STATE_INITIAL_MOCK } from '@/redux/shapes/shapes.mock';
 
 export const shapesSlice = createSlice({
   name: 'shapes',
-  initialState,
+  initialState: SHAPES_STATE_INITIAL_MOCK,
   reducers: {},
   extraReducers: builder => {
     getShapesReducer(builder);
+    getLineTypesReducer(builder);
+    getArrowTypesReducer(builder);
   },
 });
 
diff --git a/src/redux/shapes/shapes.thunks.test.ts b/src/redux/shapes/shapes.thunks.test.ts
index d4cb142cccf4f31fbb1c82b95c8ea36d6d4119cc..7b9980bf1679f1b5aac404a44e655db53b28f1d2 100644
--- a/src/redux/shapes/shapes.thunks.test.ts
+++ b/src/redux/shapes/shapes.thunks.test.ts
@@ -5,25 +5,29 @@ import {
 } 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 { bioShapesFixture } from '@/models/fixtures/bioShapesFixture';
+import { ShapesState } from '@/redux/shapes/shapes.types';
+import { lineTypesFixture } from '@/models/fixtures/lineTypesFixture';
+import { arrowTypesFixture } from '@/models/fixtures/arrowTypesFixture';
 import shapesReducer from './shapes.slice';
-import { getShapes } from './shapes.thunks';
+import { getArrowTypes, getLineTypes, getShapes } from './shapes.thunks';
 
 const mockedAxiosClient = mockNetworkNewAPIResponse();
 
 describe('shapes thunks', () => {
-  let store = {} as ToolkitStoreWithSingleSlice<BioShapesState>;
+  let store = {} as ToolkitStoreWithSingleSlice<ShapesState>;
   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);
+      mockedAxiosClient.onGet(apiPath.getShapes()).reply(HttpStatusCode.Ok, bioShapesFixture);
 
       const { payload } = await store.dispatch(getShapes());
-      expect(payload).toEqual(shapesFixture);
+      expect(payload).toEqual(bioShapesFixture);
     });
+
     it('should return undefined when data response from API is not valid ', async () => {
       mockedAxiosClient
         .onGet(apiPath.getShapes())
@@ -33,4 +37,40 @@ describe('shapes thunks', () => {
       expect(payload).toEqual(undefined);
     });
   });
+
+  describe('getLineTypes', () => {
+    it('should return data when data response from API is valid', async () => {
+      mockedAxiosClient.onGet(apiPath.getLineTypes()).reply(HttpStatusCode.Ok, lineTypesFixture);
+
+      const { payload } = await store.dispatch(getLineTypes());
+      expect(payload).toEqual(lineTypesFixture);
+    });
+
+    it('should return undefined when data response from API is not valid ', async () => {
+      mockedAxiosClient
+        .onGet(apiPath.getLineTypes())
+        .reply(HttpStatusCode.Ok, { randomProperty: 'randomValue' });
+
+      const { payload } = await store.dispatch(getLineTypes());
+      expect(payload).toEqual(undefined);
+    });
+  });
+
+  describe('getArrowTypes', () => {
+    it('should return data when data response from API is valid', async () => {
+      mockedAxiosClient.onGet(apiPath.getArrowTypes()).reply(HttpStatusCode.Ok, arrowTypesFixture);
+
+      const { payload } = await store.dispatch(getArrowTypes());
+      expect(payload).toEqual(arrowTypesFixture);
+    });
+
+    it('should return undefined when data response from API is not valid ', async () => {
+      mockedAxiosClient
+        .onGet(apiPath.getArrowTypes())
+        .reply(HttpStatusCode.Ok, { randomProperty: 'randomValue' });
+
+      const { payload } = await store.dispatch(getArrowTypes());
+      expect(payload).toEqual(undefined);
+    });
+  });
 });
diff --git a/src/redux/shapes/shapes.thunks.ts b/src/redux/shapes/shapes.thunks.ts
index 2bb0b7ecf5d6d33c63eb60b946a1d1e7ecc58822..f882d2fb6f0f64acc5b703ed77309cf1a25f0f1d 100644
--- a/src/redux/shapes/shapes.thunks.ts
+++ b/src/redux/shapes/shapes.thunks.ts
@@ -1,24 +1,58 @@
 import { bioShapeSchema } from '@/models/bioShapeSchema';
 import { apiPath } from '@/redux/apiPath';
-import { BioShape } from '@/types/models';
+import { ArrowType, BioShape, LineType } 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 {
+  ARROW_TYPES_FETCHING_ERROR_PREFIX,
+  LINE_TYPES_FETCHING_ERROR_PREFIX,
+  SHAPES_FETCHING_ERROR_PREFIX,
+} from '@/redux/shapes/shapes.constants';
 import { axiosInstanceNewAPI } from '@/services/api/utils/axiosInstance';
+import { lineTypeSchema } from '@/models/lineTypeSchema';
+import { arrowTypeSchema } from '@/models/arrowTypeSchema';
 
 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));
+      const { data } = await axiosInstanceNewAPI.get<BioShape[]>(apiPath.getShapes());
+      const isDataValid = validateDataUsingZodSchema(data, z.array(bioShapeSchema));
 
-      return isDataValid ? response.data : undefined;
+      return isDataValid ? data : undefined;
     } catch (error) {
       return Promise.reject(getError({ error, prefix: SHAPES_FETCHING_ERROR_PREFIX }));
     }
   },
 );
+
+export const getLineTypes = createAsyncThunk<LineType[] | undefined, void, ThunkConfig>(
+  'vectorMap/getLineTypes',
+  async () => {
+    try {
+      const { data } = await axiosInstanceNewAPI.get<LineType[]>(apiPath.getLineTypes());
+      const isDataValid = validateDataUsingZodSchema(data, z.array(lineTypeSchema));
+
+      return isDataValid ? data : undefined;
+    } catch (error) {
+      return Promise.reject(getError({ error, prefix: LINE_TYPES_FETCHING_ERROR_PREFIX }));
+    }
+  },
+);
+
+export const getArrowTypes = createAsyncThunk<ArrowType[] | undefined, void, ThunkConfig>(
+  'vectorMap/getArrowTypes',
+  async () => {
+    try {
+      const { data } = await axiosInstanceNewAPI.get<ArrowType[]>(apiPath.getArrowTypes());
+      const isDataValid = validateDataUsingZodSchema(data, z.array(arrowTypeSchema));
+
+      return isDataValid ? data : undefined;
+    } catch (error) {
+      return Promise.reject(getError({ error, prefix: ARROW_TYPES_FETCHING_ERROR_PREFIX }));
+    }
+  },
+);
diff --git a/src/redux/shapes/shapes.types.ts b/src/redux/shapes/shapes.types.ts
index e4b006f1906eb3ef0003fe127c6f61188ba907fd..e597d194d1ebef945cfc49da1ca881dd9dc62664 100644
--- a/src/redux/shapes/shapes.types.ts
+++ b/src/redux/shapes/shapes.types.ts
@@ -1,4 +1,14 @@
 import { FetchDataState } from '@/types/fetchDataState';
-import { BioShape } from '@/types/models';
+import { ArrowType, BioShape, LineType } from '@/types/models';
+
+export type LineTypesState = FetchDataState<LineType[], []>;
+
+export type ArrowTypesState = FetchDataState<ArrowType[], []>;
 
 export type BioShapesState = FetchDataState<BioShape[], []>;
+
+export type ShapesState = {
+  lineTypesState: LineTypesState;
+  arrowTypesState: ArrowTypesState;
+  bioShapesState: BioShapesState;
+};
diff --git a/src/redux/store.ts b/src/redux/store.ts
index 2df3467569d8cc85a234814cc001060be1ca5c99..f6b8ad65c8648756943c421cc42fd796f75e6bd7 100644
--- a/src/redux/store.ts
+++ b/src/redux/store.ts
@@ -12,6 +12,7 @@ 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 layersReducer from '@/redux/layers/layers.slice';
 import oauthReducer from '@/redux/oauth/oauth.slice';
 import overlayBioEntityReducer from '@/redux/overlayBioEntity/overlayBioEntity.slice';
 import overlaysReducer from '@/redux/overlays/overlays.slice';
@@ -63,6 +64,7 @@ export const reducers = {
   models: modelsReducer,
   shapes: shapesReducer,
   modelElements: modelElementsReducer,
+  layers: layersReducer,
   reactions: reactionsReducer,
   contextMenu: contextMenuReducer,
   cookieBanner: cookieBannerReducer,
diff --git a/src/shared/Icon/Icon.component.tsx b/src/shared/Icon/Icon.component.tsx
index b74341317ff42714f9dac5e71c159e040ccb7443..b05d1f5704360b583f6d716d55db7c26081dcb03 100644
--- a/src/shared/Icon/Icon.component.tsx
+++ b/src/shared/Icon/Icon.component.tsx
@@ -7,6 +7,7 @@ import { ChevronUpIcon } from '@/shared/Icon/Icons/ChevronUpIcon';
 import { CloseIcon } from '@/shared/Icon/Icons/CloseIcon';
 import { DotsIcon } from '@/shared/Icon/Icons/DotsIcon';
 import { ExportIcon } from '@/shared/Icon/Icons/ExportIcon';
+import { LayersIcon } from '@/shared/Icon/Icons/LayersIcon';
 import { InfoIcon } from '@/shared/Icon/Icons/InfoIcon';
 import { LegendIcon } from '@/shared/Icon/Icons/LegendIcon';
 import { PageIcon } from '@/shared/Icon/Icons/PageIcon';
@@ -40,6 +41,7 @@ const icons: Record<IconTypes, IconComponentType> = {
   dots: DotsIcon,
   admin: AdminIcon,
   export: ExportIcon,
+  layers: LayersIcon,
   info: InfoIcon,
   legend: LegendIcon,
   page: PageIcon,
diff --git a/src/shared/Icon/Icons/LayersIcon.tsx b/src/shared/Icon/Icons/LayersIcon.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..bebcbea3e67f7c083ab14369650a45523340de1b
--- /dev/null
+++ b/src/shared/Icon/Icons/LayersIcon.tsx
@@ -0,0 +1,39 @@
+interface LayersIconProps {
+  className?: string;
+}
+
+export const LayersIcon = ({ className }: LayersIconProps): JSX.Element => (
+  <svg
+    width="20"
+    height="20"
+    viewBox="0 0 24 24"
+    fill="none"
+    className={className}
+    xmlns="http://www.w3.org/2000/svg"
+  >
+    <path
+      d="M12 4L4 8.5L12 13L20 8.5L12 4Z"
+      stroke="black"
+      strokeWidth="1"
+      strokeLinecap="round"
+      strokeLinejoin="round"
+      fill="none"
+    />
+    <path
+      d="M4 12.5L12 17L20 12.5"
+      stroke="black"
+      strokeWidth="1"
+      strokeLinecap="round"
+      strokeLinejoin="round"
+      fill="none"
+    />
+    <path
+      d="M4 16.5L12 21L20 16.5"
+      stroke="black"
+      strokeWidth="1"
+      strokeLinecap="round"
+      strokeLinejoin="round"
+      fill="none"
+    />
+  </svg>
+);
diff --git a/src/types/drawerName.ts b/src/types/drawerName.ts
index d6f7961005116de9743cb35d7b2c7797de8aca3c..4a7f5114bd6949063557d0098d406b1b0a07bb2a 100644
--- a/src/types/drawerName.ts
+++ b/src/types/drawerName.ts
@@ -10,4 +10,5 @@ export type DrawerName =
   | 'overlays'
   | 'bio-entity'
   | 'comment'
-  | 'available-plugins';
+  | 'available-plugins'
+  | 'layers';
diff --git a/src/types/iconTypes.ts b/src/types/iconTypes.ts
index 07442161b637612612cfbf6aeb787733b18ebaa0..6feeea09e29c8d0c4a0eb0c46df5d514cb0dd67e 100644
--- a/src/types/iconTypes.ts
+++ b/src/types/iconTypes.ts
@@ -8,6 +8,7 @@ export type IconTypes =
   | 'dots'
   | 'admin'
   | 'export'
+  | 'layers'
   | 'info'
   | 'legend'
   | 'page'
diff --git a/src/types/models.ts b/src/types/models.ts
index e42c235b929f6e80a25975e896d38179d20d9311..13dd6e2892a7b1d5f4c7b7790b56284d73172e74 100644
--- a/src/types/models.ts
+++ b/src/types/models.ts
@@ -67,8 +67,17 @@ 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';
 import { modelElementModificationSchema } from '@/models/modelElementModificationSchema';
+import { lineTypeSchema } from '@/models/lineTypeSchema';
+import { layerSchema } from '@/models/layerSchema';
+import { layerTextSchema } from '@/models/layerTextSchema';
+import { layerRectSchema } from '@/models/layerRectSchema';
+import { pageableSchema } from '@/models/pageableSchema';
+import { modelElementSchema } from '@/models/modelElementSchema';
+import { layerOvalSchema } from '@/models/layerOvalSchema';
+import { layerLineSchema } from '@/models/layerLineSchema';
+import { arrowTypeSchema } from '@/models/arrowTypeSchema';
+import { arrowSchema } from '@/models/arrowSchema';
 
 export type Project = z.infer<typeof projectSchema>;
 export type OverviewImageView = z.infer<typeof overviewImageView>;
@@ -77,7 +86,19 @@ 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 LineType = z.infer<typeof lineTypeSchema>;
+export type ArrowType = z.infer<typeof arrowTypeSchema>;
+const layersSchema = pageableSchema(layerSchema);
+export type Layers = z.infer<typeof layersSchema>;
+export type Layer = z.infer<typeof layerSchema>;
+export type LayerText = z.infer<typeof layerTextSchema>;
+export type LayerRect = z.infer<typeof layerRectSchema>;
+export type LayerOval = z.infer<typeof layerOvalSchema>;
+export type LayerLine = z.infer<typeof layerLineSchema>;
+export type Arrow = z.infer<typeof arrowSchema>;
+const modelElementsSchema = pageableSchema(modelElementSchema);
 export type ModelElements = z.infer<typeof modelElementsSchema>;
+export type ModelElement = z.infer<typeof modelElementSchema>;
 export type Shape = z.infer<typeof shapeSchema>;
 export type Modification = z.infer<typeof modelElementModificationSchema>;
 export type MapOverlay = z.infer<typeof mapOverlay>;