From 8774c56e05f76266144b84a3994fba7b76f51744 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mi=C5=82osz=20Grocholewski?= <m.grocholewski@atcomp.pl>
Date: Wed, 11 Dec 2024 10:34:49 +0100
Subject: [PATCH] Feat/optimization

---
 .../MapViewerVector.constants.ts              |   2 +-
 .../reactionsLayer/processModelElements.ts    |   1 +
 .../reactionsLayer/useOlMapReactionsLayer.ts  |   4 +-
 .../utils/shapes/coords/getBezierCurve.ts     |   2 +-
 .../utils/shapes/coords/getEllipseCoords.ts   |   2 +-
 .../shapes/coords/getLineSegments.test.ts     |  46 ++++
 .../utils/shapes/coords/getLineSegments.ts    |  21 ++
 .../utils/shapes/coords/getPolygonCoords.ts   |   2 +-
 .../utils/shapes/elements/BaseMultiPolygon.ts |  71 +++----
 .../utils/shapes/elements/Compartment.ts      |  30 ++-
 .../shapes/elements/CompartmentCircle.test.ts |   1 +
 .../shapes/elements/CompartmentCircle.ts      |   7 +-
 .../elements/CompartmentPathway.test.ts       |   1 +
 .../shapes/elements/CompartmentPathway.ts     |  13 +-
 .../shapes/elements/CompartmentSquare.test.ts |   1 +
 .../shapes/elements/CompartmentSquare.ts      |   3 +
 .../utils/shapes/elements/MapElement.ts       |  29 ++-
 .../utils/shapes/elements/getArrowFeature.ts  |   9 +
 .../utils/shapes/layer/Layer.ts               |  23 +-
 .../utils/shapes/reaction/Reaction.test.ts    |   5 +-
 .../utils/shapes/reaction/Reaction.ts         | 197 +++++++++++-------
 .../shapes/style/getScaledElementStyle.ts     |  27 ++-
 .../Map/MapViewer/utils/useOlMap.ts           |   2 +-
 23 files changed, 342 insertions(+), 157 deletions(-)
 create mode 100644 src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getLineSegments.test.ts
 create mode 100644 src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getLineSegments.ts

diff --git a/src/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants.ts b/src/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants.ts
index 79fab6da..5bc52a7f 100644
--- a/src/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants.ts
@@ -2,7 +2,7 @@ import { Color, ShapeRelAbs, ShapeRelAbsBezierPoint } from '@/types/models';
 
 export const VECTOR_MAP_LAYER_TYPE = 'vectorMapLayer';
 
-export const COMPLEX_SBO_TERM = 'SBO:0000253';
+export const COMPLEX_SBO_TERMS = ['SBO:0000253', 'SBO:0000297', 'SBO:0000289'];
 
 export const WHITE_COLOR: Color = {
   alpha: 255,
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/processModelElements.ts b/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/processModelElements.ts
index 2e5bbf99..2f5b97ba 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/processModelElements.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/processModelElements.ts
@@ -54,6 +54,7 @@ export default function processModelElements(
     if (element.sboTerm === 'SBO:0000290') {
       const compartmentProps = {
         id: element.id,
+        sboTerm: element.sboTerm,
         complexId: element.complex,
         compartmentId: element.compartment,
         x: element.x,
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 6f2aafa1..bf0d3389 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/useOlMapReactionsLayer.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/useOlMapReactionsLayer.ts
@@ -178,7 +178,7 @@ export const useOlMapReactionsLayer = ({
         vectorSource,
         mapInstance,
       });
-      return reactionObject.features;
+      return [reactionObject.lineFeature, ...reactionObject.reactionFeatures];
     });
   }, [
     reactionsForCurrentModel,
@@ -244,8 +244,6 @@ export const useOlMapReactionsLayer = ({
   return useMemo(() => {
     const vectorLayer = new VectorLayer({
       source: vectorSource,
-      updateWhileAnimating: true,
-      updateWhileInteracting: true,
     });
     vectorLayer.set('type', VECTOR_MAP_LAYER_TYPE);
     return vectorLayer;
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getBezierCurve.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getBezierCurve.ts
index 814e7aac..b086befa 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getBezierCurve.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getBezierCurve.ts
@@ -6,7 +6,7 @@ export default function getBezierCurve({
   p1,
   p2,
   p3,
-  numPoints = 50,
+  numPoints = 3,
 }: {
   p0: Coordinate;
   p1: Coordinate;
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getEllipseCoords.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getEllipseCoords.ts
index aca8a892..c617ffaf 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getEllipseCoords.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getEllipseCoords.ts
@@ -13,7 +13,7 @@ export default function getEllipseCoords({
   height,
   width,
   pointToProjection,
-  points = 20,
+  points = 30,
 }: {
   x: number;
   y: number;
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getLineSegments.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getLineSegments.test.ts
new file mode 100644
index 00000000..ec6d5a70
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getLineSegments.test.ts
@@ -0,0 +1,46 @@
+/* eslint-disable no-magic-numbers */
+import { Line } from '@/types/models';
+import { BLACK_COLOR } from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants';
+import getLineSegments from './getLineSegments';
+
+describe('getLineSegments', () => {
+  it('should return flattened array of projected coordinates for all line segments', () => {
+    const pointToProjection = jest.fn(({ x, y }) => [x * 10, y * 10]);
+
+    const line = {
+      id: 1,
+      width: 1,
+      color: BLACK_COLOR,
+      z: 1,
+      startArrow: {
+        arrowType: 'NONE',
+        angle: 2.748893571891069,
+        lineType: 'SOLID',
+        length: 15.0,
+      },
+      endArrow: {
+        arrowType: 'NONE',
+        angle: 2.748893571891069,
+        lineType: 'SOLID',
+        length: 15.0,
+      },
+      segments: [
+        { x1: 1, y1: 2, x2: 3, y2: 4 },
+        { x1: 3, y1: 4, x2: 5, y2: 6 },
+      ],
+    } as Line;
+
+    const result = getLineSegments(line, pointToProjection);
+
+    expect(pointToProjection).toHaveBeenCalledTimes(3);
+    expect(pointToProjection).toHaveBeenNthCalledWith(1, { x: 1, y: 2 });
+    expect(pointToProjection).toHaveBeenNthCalledWith(2, { x: 3, y: 4 });
+    expect(pointToProjection).toHaveBeenNthCalledWith(3, { x: 5, y: 6 });
+
+    expect(result).toEqual([
+      [10, 20],
+      [30, 40],
+      [50, 60],
+    ]);
+  });
+});
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getLineSegments.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getLineSegments.ts
new file mode 100644
index 00000000..c7afcc53
--- /dev/null
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getLineSegments.ts
@@ -0,0 +1,21 @@
+/* eslint-disable no-magic-numbers */
+import { Coordinate } from 'ol/coordinate';
+import { Line } from '@/types/models';
+import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
+
+export default function getLineSegments(
+  line: Line,
+  pointToProjection: UsePointToProjectionResult,
+): Array<Coordinate> {
+  return 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();
+}
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getPolygonCoords.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getPolygonCoords.ts
index 69af893a..9f68ed47 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getPolygonCoords.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getPolygonCoords.ts
@@ -47,6 +47,6 @@ export default function getPolygonCoords({
     const { p1, p2, p3 } = getCurveCoords({ x, y, point, height, width, pointToProjection });
     const p0 = lastPoint;
     lastPoint = p3;
-    return getBezierCurve({ p0, p1, p2, p3, numPoints: 20 });
+    return getBezierCurve({ p0, p1, p2, p3, numPoints: 3 });
   });
 }
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/BaseMultiPolygon.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/BaseMultiPolygon.ts
index 351fef81..c13c61f4 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/BaseMultiPolygon.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/BaseMultiPolygon.ts
@@ -1,12 +1,11 @@
 /* eslint-disable no-magic-numbers */
 import Polygon from 'ol/geom/Polygon';
-import { Style } from 'ol/style';
+import { Stroke, Style } from 'ol/style';
 import Feature, { FeatureLike } from 'ol/Feature';
 import { MultiPolygon } from 'ol/geom';
 import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
 import {
   HorizontalAlign,
-  ScaleFunction,
   VerticalAlign,
 } from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.types';
 import { MapInstance } from '@/types/map';
@@ -15,7 +14,7 @@ import { rgbToHex } from '@/components/Map/MapViewer/MapViewerVector/utils/shape
 import getTextCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextCoords';
 import { Color } from '@/types/models';
 import {
-  COMPLEX_SBO_TERM,
+  COMPLEX_SBO_TERMS,
   MAP_ELEMENT_TYPES,
 } from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants';
 import VectorSource from 'ol/source/Vector';
@@ -27,7 +26,7 @@ import handleSemanticView from '@/components/Map/MapViewer/MapViewerVector/utils
 
 export interface BaseMapElementProps {
   type: string;
-  sboTerm?: string;
+  sboTerm: string;
   id: number;
   complexId?: number | null;
   compartmentId: number | null;
@@ -52,12 +51,13 @@ export interface BaseMapElementProps {
   vectorSource: VectorSource;
   mapBackgroundType: number;
   mapSize: MapSize;
+  mapInstance: MapInstance;
 }
 
 export default abstract class BaseMultiPolygon {
   type: string;
 
-  sboTerm: string | undefined;
+  sboTerm: string;
 
   id: number;
 
@@ -118,6 +118,8 @@ export default abstract class BaseMultiPolygon {
     [number, number, number, number]
   >();
 
+  minResolution: number;
+
   constructor({
     type,
     sboTerm,
@@ -145,6 +147,7 @@ export default abstract class BaseMultiPolygon {
     vectorSource,
     mapBackgroundType,
     mapSize,
+    mapInstance,
   }: BaseMapElementProps) {
     this.type = type;
     this.sboTerm = sboTerm;
@@ -172,6 +175,9 @@ export default abstract class BaseMultiPolygon {
     this.vectorSource = vectorSource;
     this.mapBackgroundType = mapBackgroundType;
     this.mapSize = mapSize;
+
+    const maxZoom = mapInstance?.getView().get('originalMaxZoom');
+    this.minResolution = mapInstance?.getView().getResolutionForZoom(maxZoom) || 1;
   }
 
   protected abstract createPolygons(): void;
@@ -200,6 +206,7 @@ export default abstract class BaseMultiPolygon {
         horizontalAlign: this.nameHorizontalAlign,
       });
       textStyle.setGeometry(textPolygon);
+      textPolygon.set('style', textStyle);
       this.styles.push(textStyle);
       this.polygons.push(textPolygon);
     }
@@ -209,19 +216,6 @@ export default abstract class BaseMultiPolygon {
     this.feature = new Feature({
       geometry: new MultiPolygon(this.polygons),
       zIndex: this.zIndex,
-      getScale: ((): ScaleFunction => {
-        const maxZoom = mapInstance?.getView().get('originalMaxZoom');
-        const minResolution = maxZoom
-          ? mapInstance?.getView().getResolutionForZoom(maxZoom)
-          : undefined;
-
-        return (resolution: number): number => {
-          if (minResolution) {
-            return minResolution / resolution;
-          }
-          return 1;
-        };
-      })(),
       getMapExtent: (resolution: number): [number, number, number, number] | undefined => {
         if (this.mapExtentCache.has(resolution)) {
           return this.mapExtentCache.get(resolution);
@@ -262,14 +256,15 @@ export default abstract class BaseMultiPolygon {
       return undefined;
     }
     const styles: Array<Style> = [];
-    const getScale = feature.get('getScale');
-    let scale = 1;
+    const scale = this.minResolution / resolution;
     let cover = false;
     let largestExtent: Extent | null;
 
-    if (getScale instanceof Function) {
-      scale = getScale(resolution);
+    if (this.complexId && !COMPLEX_SBO_TERMS.includes(this.sboTerm) && scale < 0.215) {
+      feature.set('hidden', true);
+      return [];
     }
+    feature.set('hidden', false);
 
     let hide = false;
     if (this.mapBackgroundType === MapBackgroundsEnum.SEMANTIC) {
@@ -292,9 +287,9 @@ export default abstract class BaseMultiPolygon {
 
     let type: string;
     let fontSize: number;
-    let lineWidth: number;
     let text: string;
     let coverStyle: Style | undefined;
+    let strokeStyle: Stroke | undefined;
 
     this.styles.forEach(style => {
       const styleGeometry = style.getGeometry();
@@ -302,8 +297,8 @@ export default abstract class BaseMultiPolygon {
         type = styleGeometry.get('type');
         text = styleGeometry.get('text');
         fontSize = styleGeometry.get('fontSize') || 10;
-        lineWidth = styleGeometry.get('lineWidth');
         coverStyle = styleGeometry.get('coverStyle');
+        strokeStyle = styleGeometry.get('strokeStyle');
       }
 
       if (cover) {
@@ -329,30 +324,36 @@ export default abstract class BaseMultiPolygon {
         return;
       }
 
-      const clonedStyle = style.clone();
-      const textStyle = clonedStyle.getText();
-      const strokeStyle = clonedStyle.getStroke();
+      const textStyle = style.getText();
       if (type === 'text' && textStyle) {
         textStyle.setScale(scale);
         textStyle.setText(text);
       }
-      if (strokeStyle && lineWidth) {
+      if (strokeStyle) {
+        const lineWidth = strokeStyle.getWidth() || 1;
         if (
           !this.overlaysVisible &&
-          lineWidth * scale < 0.08 &&
-          this.sboTerm !== COMPLEX_SBO_TERM
+          scale < 0.18 &&
+          !COMPLEX_SBO_TERMS.includes(this.sboTerm) &&
+          this.type !== MAP_ELEMENT_TYPES.COMPARTMENT
         ) {
-          clonedStyle.setStroke(null);
+          style.setStroke(null);
         } else {
-          strokeStyle.setWidth(lineWidth * scale);
           const lineDash = strokeStyle.getLineDash();
+          let newLineDash: Array<number> = [];
           if (lineDash) {
-            const newLineDash = lineDash.map(width => width * scale);
-            strokeStyle.setLineDash(newLineDash);
+            newLineDash = lineDash.map(width => width * scale);
           }
+          const newStrokeStyle = new Stroke({
+            color: strokeStyle.getColor(),
+            width: lineWidth * scale,
+            lineDash: newLineDash,
+          });
+
+          style.setStroke(newStrokeStyle);
         }
       }
-      styles.push(clonedStyle);
+      styles.push(style);
     });
 
     return 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
index b851e10a..8906e75d 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Compartment.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Compartment.ts
@@ -7,7 +7,7 @@ import {
 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 { Stroke, 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';
@@ -24,6 +24,7 @@ export interface CompartmentProps {
   id: number;
   complexId?: number | null;
   compartmentId: number | null;
+  sboTerm: string;
   x: number;
   y: number;
   width: number;
@@ -68,6 +69,7 @@ export default abstract class Compartment extends BaseMultiPolygon {
     id,
     complexId,
     compartmentId,
+    sboTerm,
     x,
     y,
     width,
@@ -99,6 +101,7 @@ export default abstract class Compartment extends BaseMultiPolygon {
       id,
       complexId,
       compartmentId,
+      sboTerm,
       x,
       y,
       width,
@@ -120,6 +123,7 @@ export default abstract class Compartment extends BaseMultiPolygon {
       vectorSource,
       mapBackgroundType,
       mapSize,
+      mapInstance,
     });
     this.outerWidth = outerWidth;
     this.innerWidth = innerWidth;
@@ -161,13 +165,17 @@ export default abstract class Compartment extends BaseMultiPolygon {
 
     const outerPolygon = new Polygon([this.outerCoords]);
     outerPolygon.set('type', MAP_ELEMENT_TYPES.COMPARTMENT);
-    outerPolygon.set('lineWidth', this.outerWidth);
+    let outerPolygonStroke: Stroke | undefined;
+    if (this.overlaysVisible) {
+      outerPolygonStroke = getStroke({ width: this.outerWidth });
+    } else {
+      outerPolygonStroke = getStroke({ color: rgbToHex(this.borderColor), width: this.outerWidth });
+    }
+    outerPolygon.set('strokeStyle', outerPolygonStroke);
     this.styles.push(
       new Style({
         geometry: outerPolygon,
-        stroke: this.overlaysVisible
-          ? getStroke({ width: this.outerWidth })
-          : getStroke({ color: rgbToHex(this.borderColor), width: this.outerWidth }),
+        stroke: outerPolygonStroke,
         zIndex: this.zIndex,
       }),
     );
@@ -175,13 +183,17 @@ export default abstract class Compartment extends BaseMultiPolygon {
 
     const innerPolygon = new Polygon([this.innerCoords]);
     innerPolygon.set('type', MAP_ELEMENT_TYPES.COMPARTMENT);
-    innerPolygon.set('lineWidth', this.innerWidth);
+    let innerPolygonStroke: Stroke | undefined;
+    if (this.overlaysVisible) {
+      innerPolygonStroke = getStroke({ width: this.innerWidth });
+    } else {
+      innerPolygonStroke = getStroke({ color: rgbToHex(this.borderColor), width: this.innerWidth });
+    }
+    innerPolygon.set('strokeStyle', innerPolygonStroke);
     this.styles.push(
       new Style({
         geometry: innerPolygon,
-        stroke: this.overlaysVisible
-          ? getStroke({ width: this.innerWidth })
-          : getStroke({ color: rgbToHex(this.borderColor), width: this.innerWidth }),
+        stroke: innerPolygonStroke,
         fill: this.overlaysVisible
           ? getFill({ color: rgbToHex(TRANSPARENT_COLOR) })
           : getFill({ color: rgbToHex({ ...this.fillColor, alpha: 9 }) }),
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
index 876dd762..7c5956c6 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentCircle.test.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentCircle.test.ts
@@ -46,6 +46,7 @@ describe('CompartmentCircle', () => {
       id: 1,
       complexId: null,
       compartmentId: null,
+      sboTerm: 'SBO:0000253',
       x: 0,
       y: 0,
       width: 100,
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentCircle.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentCircle.ts
index 3dd38183..94a65df8 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentCircle.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentCircle.ts
@@ -21,6 +21,7 @@ export type CompartmentCircleProps = {
   id: number;
   complexId?: number | null;
   compartmentId: number | null;
+  sboTerm: string;
   x: number;
   y: number;
   width: number;
@@ -53,6 +54,7 @@ export default class CompartmentCircle extends Compartment {
     id,
     complexId,
     compartmentId,
+    sboTerm,
     x,
     y,
     width,
@@ -83,6 +85,7 @@ export default class CompartmentCircle extends Compartment {
       id,
       complexId,
       compartmentId,
+      sboTerm,
       x,
       y,
       width,
@@ -119,7 +122,7 @@ export default class CompartmentCircle extends Compartment {
       radius: COMPARTMENT_CIRCLE_RADIUS,
       height: this.height,
       width: this.width,
-      points: 40,
+      points: 36,
       pointToProjection: this.pointToProjection,
     });
     this.innerCoords = getEllipseCoords({
@@ -129,7 +132,7 @@ export default class CompartmentCircle extends Compartment {
       radius: COMPARTMENT_CIRCLE_RADIUS,
       height: this.height - 2 * this.thickness,
       width: this.width - 2 * this.thickness,
-      points: 40,
+      points: 20,
       pointToProjection: this.pointToProjection,
     });
   }
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentPathway.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentPathway.test.ts
index 5e1df810..00c1931b 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentPathway.test.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentPathway.test.ts
@@ -46,6 +46,7 @@ describe('CompartmentPathway', () => {
       id: 1,
       complexId: null,
       compartmentId: null,
+      sboTerm: 'SBO:0000253',
       x: 0,
       y: 0,
       width: 100,
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentPathway.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentPathway.ts
index d51153e6..e758a681 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentPathway.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentPathway.ts
@@ -20,11 +20,13 @@ 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 { MapSize } from '@/redux/map/map.types';
+import getStroke from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStroke';
 
 export type CompartmentPathwayProps = {
   id: number;
   complexId?: number | null;
   compartmentId: number | null;
+  sboTerm: string;
   x: number;
   y: number;
   width: number;
@@ -59,6 +61,7 @@ export default class CompartmentPathway extends BaseMultiPolygon {
     id,
     complexId,
     compartmentId,
+    sboTerm,
     x,
     y,
     width,
@@ -88,6 +91,7 @@ export default class CompartmentPathway extends BaseMultiPolygon {
       id,
       complexId,
       compartmentId,
+      sboTerm,
       x,
       y,
       width,
@@ -109,6 +113,7 @@ export default class CompartmentPathway extends BaseMultiPolygon {
       vectorSource,
       mapBackgroundType,
       mapSize,
+      mapInstance,
     });
     this.outerWidth = outerWidth;
     this.overlaysVisible = overlaysVisible;
@@ -128,12 +133,18 @@ export default class CompartmentPathway extends BaseMultiPolygon {
       ],
     ]);
     compartmentPolygon.set('type', MAP_ELEMENT_TYPES.COMPARTMENT);
-    compartmentPolygon.set('lineWidth', this.outerWidth);
     const coverStyle = new Style({
       geometry: compartmentPolygon,
       fill: getFill({ color: rgbToHex({ ...this.fillColor, alpha: 255 }) }),
     });
     compartmentPolygon.set('coverStyle', coverStyle);
+    compartmentPolygon.set(
+      'strokeStyle',
+      getStroke({
+        color: rgbToHex(this.borderColor),
+        width: this.outerWidth,
+      }),
+    );
     this.styles.push(
       getStyle({
         geometry: compartmentPolygon,
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
index c7721148..53fff2ff 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentSquare.test.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentSquare.test.ts
@@ -44,6 +44,7 @@ describe('CompartmentSquare', () => {
       id: 1,
       complexId: null,
       compartmentId: null,
+      sboTerm: 'SBO:0000253',
       x: 0,
       y: 0,
       width: 100,
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentSquare.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentSquare.ts
index b4905d61..8f033df3 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentSquare.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentSquare.ts
@@ -20,6 +20,7 @@ export type CompartmentSquareProps = {
   id: number;
   complexId?: number | null;
   compartmentId: number | null;
+  sboTerm: string;
   x: number;
   y: number;
   width: number;
@@ -52,6 +53,7 @@ export default class CompartmentSquare extends Compartment {
     id,
     complexId,
     compartmentId,
+    sboTerm,
     x,
     y,
     width,
@@ -82,6 +84,7 @@ export default class CompartmentSquare extends Compartment {
       id,
       complexId,
       compartmentId,
+      sboTerm,
       x,
       y,
       width,
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement.ts
index 6ef7a733..77ab7ebc 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement.ts
@@ -163,6 +163,7 @@ export default class MapElement extends BaseMultiPolygon {
       overlaysVisible,
       mapBackgroundType,
       mapSize,
+      mapInstance,
     });
     this.shapes = shapes;
     this.lineWidth = lineWidth;
@@ -221,13 +222,14 @@ export default class MapElement extends BaseMultiPolygon {
       });
       modificationPolygon.set('type', MAP_ELEMENT_TYPES.MODIFICATION);
       modificationPolygon.set('fontSize', modification.fontSize);
-      modificationPolygon.set('lineWidth', 1);
+      const modificationStrokeStyle = getStroke({ color: rgbToHex(modification.borderColor) });
       const modificationStyle = new Style({
         geometry: modificationPolygon,
-        stroke: getStroke({ color: rgbToHex(modification.borderColor) }),
+        stroke: modificationStrokeStyle,
         fill: getFill({ color: rgbToHex(modification.fillColor) }),
         zIndex: modification.z,
       });
+      modificationPolygon.set('strokeStyle', modificationStrokeStyle);
       this.polygons.push(modificationPolygon);
       this.styles.push(modificationStyle);
     });
@@ -276,13 +278,18 @@ export default class MapElement extends BaseMultiPolygon {
         pointToProjection: this.pointToProjection,
       });
       activityBorderPolygon.set('type', MAP_ELEMENT_TYPES.ACTIVITY_BORDER);
-      activityBorderPolygon.set('lineWidth', 1);
       const activityBorderStyle = getStyle({
         geometry: activityBorderPolygon,
         fillColor: TRANSPARENT_COLOR,
         lineDash: [3, 5],
         zIndex: this.zIndex,
       });
+      activityBorderPolygon.set(
+        'strokeStyle',
+        getStroke({
+          lineDash: [3, 5],
+        }),
+      );
       this.polygons.push(activityBorderPolygon);
       this.styles.push(activityBorderStyle);
     });
@@ -299,7 +306,6 @@ export default class MapElement extends BaseMultiPolygon {
         pointToProjection: this.pointToProjection,
       });
       elementPolygon.set('type', MAP_ELEMENT_TYPES.ENTITY);
-      elementPolygon.set('lineWidth', this.lineWidth);
       const elementStyle = getStyle({
         geometry: elementPolygon,
         borderColor: this.borderColor,
@@ -308,6 +314,14 @@ export default class MapElement extends BaseMultiPolygon {
         lineDash: this.lineDash,
         zIndex: this.zIndex,
       });
+      elementPolygon.set(
+        'strokeStyle',
+        getStroke({
+          color: rgbToHex(this.borderColor),
+          width: this.lineWidth,
+          lineDash: this.lineDash,
+        }),
+      );
       this.polygons.push(elementPolygon);
       this.styles.push(elementStyle);
     });
@@ -335,13 +349,18 @@ export default class MapElement extends BaseMultiPolygon {
         ],
       ]);
       polygon.set('type', MAP_ELEMENT_TYPES.OVERLAY);
-      polygon.set('lineWidth', 1);
       const style = getStyle({
         geometry: polygon,
         borderColor: color,
         fillColor: color,
         zIndex: this.zIndex,
       });
+      polygon.set(
+        'strokeStyle',
+        getStroke({
+          color,
+        }),
+      );
       this.polygons.push(polygon);
       this.styles.push(style);
     });
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getArrowFeature.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getArrowFeature.ts
index 02e0ab19..c8f46d9d 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getArrowFeature.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getArrowFeature.ts
@@ -9,6 +9,8 @@ import getShapePolygon from '@/components/Map/MapViewer/MapViewerVector/utils/sh
 import Polygon from 'ol/geom/Polygon';
 import { WHITE_COLOR } from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants';
 import { ArrowTypeDict } from '@/redux/shapes/shapes.types';
+import getStroke from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStroke';
+import { rgbToHex } from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/rgbToHex';
 
 export default function getArrowFeature({
   arrowTypes,
@@ -53,6 +55,13 @@ export default function getArrowFeature({
       fillColor: shape.fill === false ? WHITE_COLOR : color,
       lineWidth,
     });
+    arrowPolygon.set(
+      'strokeStyle',
+      getStroke({
+        color: rgbToHex(color),
+        width: lineWidth,
+      }),
+    );
     arrowPolygon.rotate(rotation, pointToProjection({ x, y }));
     arrowStyles.push(style);
     arrowPolygons.push(arrowPolygon);
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/Layer.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/Layer.ts
index ec4cab79..a842f5f2 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/Layer.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/Layer.ts
@@ -24,6 +24,7 @@ import {
   TRANSPARENT_COLOR,
 } from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants';
 import getScaledElementStyle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getScaledElementStyle';
+import { Stroke } from 'ol/style';
 
 export interface LayerProps {
   texts: Array<LayerText>;
@@ -171,7 +172,7 @@ export default class Layer {
         height: oval.height,
         width: oval.width,
         pointToProjection: this.pointToProjection,
-        points: 36,
+        points: 20,
       });
       const polygon = new Polygon([coords]);
       const polygonStyle = getStyle({
@@ -264,7 +265,6 @@ export default class Layer {
         });
         if (endArrowFeature) {
           endArrowFeature.set('elementType', LAYER_ELEMENT_TYPES.ARROW);
-          endArrowFeature.set('lineWidth', line.width);
           endArrowFeature.setStyle(this.getStyle.bind(this));
           arrowsFeatures.push(endArrowFeature);
         }
@@ -302,22 +302,27 @@ export default class Layer {
     }
 
     const scale = minResolution / resolution;
-    const lineWidth = feature.get('lineWidth');
+    let strokeStyle: Stroke | undefined;
     const type = feature.get('elementType');
 
     if (type === LAYER_ELEMENT_TYPES.ARROW && scale <= 0.08) {
       return [];
     }
 
+    const stylesToProcess: Array<Style> = [];
     if (style instanceof Style) {
-      styles.push(getScaledElementStyle(style, lineWidth, scale));
+      stylesToProcess.push(style);
     } else if (Array.isArray(style)) {
-      style.forEach(singleStyle => {
-        if (singleStyle instanceof Style) {
-          styles.push(getScaledElementStyle(singleStyle, lineWidth, scale));
-        }
-      });
+      stylesToProcess.push(...style);
     }
+    stylesToProcess.forEach(singleStyle => {
+      const styleGeometry = singleStyle.getGeometry();
+      if (styleGeometry instanceof Polygon || styleGeometry instanceof LineString) {
+        strokeStyle = styleGeometry.get('strokeStyle');
+      }
+      styles.push(getScaledElementStyle(singleStyle, strokeStyle, scale));
+    });
+
     return styles;
   }
 }
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction.test.ts
index 70c5b3e4..0e6dce24 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction.test.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction.test.ts
@@ -51,7 +51,8 @@ describe('Layer', () => {
   it('should initialize a Reaction class', () => {
     const reaction = new Reaction(props);
 
-    expect(reaction.features.length).toBe(12);
-    expect(reaction.features).toBeInstanceOf(Array<Feature>);
+    expect(reaction.reactionFeatures.length).toBe(3);
+    expect(reaction.reactionFeatures).toBeInstanceOf(Array<Feature>);
+    expect(reaction.lineFeature).toBeInstanceOf(Feature);
   });
 });
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction.ts
index 43310864..afeeeeab 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction.ts
@@ -2,11 +2,8 @@
 import { Line, Operator, ReactionProduct, Shape } from '@/types/models';
 import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
 import { Feature } from 'ol';
-import { Circle, LineString, MultiPolygon } from 'ol/geom';
-import getRotation from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getRotation';
+import { Circle, LineString, MultiLineString, MultiPolygon } from 'ol/geom';
 import getStyle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStyle';
-import getArrowFeature from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getArrowFeature';
-import getShapePolygon from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getShapePolygon';
 import Polygon from 'ol/geom/Polygon';
 import Style from 'ol/style/Style';
 import {
@@ -15,12 +12,18 @@ import {
 } from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants';
 import { FeatureLike } from 'ol/Feature';
 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 { ArrowTypeDict, LineTypeDict } from '@/redux/shapes/shapes.types';
 import { FEATURE_TYPE } from '@/constants/features';
 import getScaledElementStyle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getScaledElementStyle';
 import VectorSource from 'ol/source/Vector';
+import getStroke from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStroke';
+import { Stroke } from 'ol/style';
+import getLineSegments from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getLineSegments';
+import getRotation from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getRotation';
+import getArrowFeature from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getArrowFeature';
+import getShapePolygon from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getShapePolygon';
+import getTextStyle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextStyle';
 
 export interface ReactionProps {
   id: number;
@@ -63,9 +66,13 @@ export default class Reaction {
 
   vectorSource: VectorSource;
 
-  mapInstance: MapInstance;
+  lineFeature: Feature<MultiLineString> = new Feature();
+
+  reactionFeatures: Array<Feature<MultiPolygon> | Feature<Circle>> = [];
 
-  features: Array<Feature> = [];
+  lineStyles: Array<Style> = [];
+
+  minResolution: number;
 
   constructor({
     id,
@@ -94,56 +101,51 @@ export default class Reaction {
     this.shapes = shapes;
     this.pointToProjection = pointToProjection;
     this.vectorSource = vectorSource;
-    this.mapInstance = mapInstance;
+
+    const maxZoom = mapInstance?.getView().get('originalMaxZoom');
+    this.minResolution = mapInstance?.getView().getResolutionForZoom(maxZoom) || 1;
 
     this.drawReaction();
   }
 
   private drawReaction(): void {
     const reactionSquareFeature = this.getReactionSquare();
-    this.features.push(reactionSquareFeature);
-    let lineFeature = this.getLineFeature(this.line);
-    this.features.push(lineFeature.lineFeature);
-    this.features.push(...lineFeature.arrowsFeatures);
-    this.products.forEach(product => {
-      lineFeature = this.getLineFeature(product.line);
-      this.features.push(lineFeature.lineFeature);
-      this.features.push(...lineFeature.arrowsFeatures);
+    this.reactionFeatures.push(reactionSquareFeature);
+
+    const lineStringElements: Array<LineString> = [];
+    let lineStringWithArrows = this.getLineStringWithArrows(this.line);
+    lineStringElements.push(lineStringWithArrows.lineString);
+    this.reactionFeatures.push(...lineStringWithArrows.arrowsFeatures);
+    [...this.products, ...this.reactants, ...this.modifiers].forEach(element => {
+      lineStringWithArrows = this.getLineStringWithArrows(element.line);
+      lineStringElements.push(lineStringWithArrows.lineString);
+      this.reactionFeatures.push(...lineStringWithArrows.arrowsFeatures);
     });
-    this.reactants.forEach(reactant => {
-      lineFeature = this.getLineFeature(reactant.line);
-      this.features.push(lineFeature.lineFeature);
-      this.features.push(...lineFeature.arrowsFeatures);
+    [...this.operators].forEach(operator => {
+      lineStringWithArrows = this.getLineStringWithArrows(operator.line);
+      lineStringElements.push(lineStringWithArrows.lineString);
+      this.reactionFeatures.push(...lineStringWithArrows.arrowsFeatures);
+      this.reactionFeatures.push(this.getOperator(operator));
     });
-    this.operators.forEach(operator => {
-      lineFeature = this.getLineFeature(operator.line);
-      this.features.push(lineFeature.lineFeature);
-      this.features.push(...lineFeature.arrowsFeatures);
-      this.features.push(this.getOperator(operator));
-    });
-    this.modifiers.forEach(modifier => {
-      lineFeature = this.getLineFeature(modifier.line);
-      this.features.push(lineFeature.lineFeature);
-      this.features.push(...lineFeature.arrowsFeatures);
+
+    const multiLineString = new MultiLineString(lineStringElements);
+
+    this.lineFeature = new Feature<MultiLineString>({
+      geometry: multiLineString,
+      id: this.id,
+      type: FEATURE_TYPE.REACTION,
+      elementType: REACTION_ELEMENT_TYPES.LINE,
+      zIndex: this.zIndex,
     });
+    this.lineFeature.setStyle(this.getLineStyle.bind(this));
   }
 
-  private getLineFeature(line: Line): {
-    lineFeature: Feature<LineString>;
+  private getLineStringWithArrows(line: Line): {
+    lineString: LineString;
     arrowsFeatures: Array<Feature<MultiPolygon>>;
   } {
     const arrowsFeatures: Array<Feature<MultiPolygon>> = [];
-    const points = line.segments
-      .map((segment, index) => {
-        if (index === 0) {
-          return [
-            this.pointToProjection({ x: segment.x1, y: segment.y1 }),
-            this.pointToProjection({ x: segment.x2, y: segment.y2 }),
-          ];
-        }
-        return [this.pointToProjection({ x: segment.x2, y: segment.y2 })];
-      })
-      .flat();
+    const points = getLineSegments(line, this.pointToProjection);
 
     if (line.startArrow.arrowType !== 'NONE') {
       const firstSegment = line.segments[0];
@@ -168,8 +170,7 @@ export default class Reaction {
       });
       if (startArrowFeature) {
         startArrowFeature.set('elementType', REACTION_ELEMENT_TYPES.ARROW);
-        startArrowFeature.set('lineWidth', line.width);
-        startArrowFeature.setStyle(this.getStyle.bind(this));
+        startArrowFeature.setStyle(this.getReactionObjectStyle.bind(this));
         arrowsFeatures.push(startArrowFeature);
       }
     }
@@ -196,8 +197,7 @@ export default class Reaction {
       });
       if (endArrowFeature) {
         endArrowFeature.set('elementType', REACTION_ELEMENT_TYPES.ARROW);
-        endArrowFeature.set('lineWidth', line.width);
-        endArrowFeature.setStyle(this.getStyle.bind(this));
+        endArrowFeature.setStyle(this.getReactionObjectStyle.bind(this));
         arrowsFeatures.push(endArrowFeature);
       }
     }
@@ -212,18 +212,17 @@ export default class Reaction {
       lineDash,
       zIndex: this.zIndex,
     });
-    const lineFeature = new Feature<LineString>({
-      geometry: lineString,
-      style: lineStyle,
-      lineWidth: line.width,
-      id: this.id,
-      type: FEATURE_TYPE.REACTION,
-      elementType: REACTION_ELEMENT_TYPES.LINE,
-      zIndex: this.zIndex,
-    });
-    lineFeature.setStyle(this.getStyle.bind(this));
+    lineString.set(
+      'strokeStyle',
+      getStroke({
+        color: rgbToHex(line.color),
+        width: line.width,
+        lineDash,
+      }),
+    );
+    this.lineStyles.push(lineStyle);
 
-    return { lineFeature, arrowsFeatures };
+    return { lineString, arrowsFeatures };
   }
 
   private getReactionSquare(): Feature<MultiPolygon> {
@@ -252,6 +251,13 @@ export default class Reaction {
         borderColor: this.line.color,
         zIndex: this.zIndex + 1,
       });
+      squarePolygon.set(
+        'strokeStyle',
+        getStroke({
+          color: rgbToHex(this.line.color),
+          width: this.line.width,
+        }),
+      );
       squarePolygon.rotate(
         squareRotation,
         this.pointToProjection({
@@ -271,7 +277,7 @@ export default class Reaction {
       elementType: REACTION_ELEMENT_TYPES.SQUARE,
       zIndex: this.zIndex,
     });
-    squareFeature.setStyle(this.getStyle.bind(this));
+    squareFeature.setStyle(this.getReactionObjectStyle.bind(this));
     return squareFeature;
   }
 
@@ -304,6 +310,12 @@ export default class Reaction {
       borderColor: operator.line.color,
       fillColor: operator.line.color,
     });
+    circle.set(
+      'strokeStyle',
+      getStroke({
+        color: rgbToHex(operator.line.color),
+      }),
+    );
 
     if (operator.operatorText) {
       circleStyle.getFill()?.setColor(rgbToHex(WHITE_COLOR));
@@ -328,7 +340,7 @@ export default class Reaction {
       fontSize: 10,
       zIndex: this.zIndex,
     });
-    circleFeature.setStyle(this.getStyle.bind(this));
+    circleFeature.setStyle(this.getReactionObjectStyle.bind(this));
     return circleFeature;
   }
 
@@ -339,7 +351,7 @@ export default class Reaction {
     });
   }
 
-  protected getStyle(feature: FeatureLike, resolution: number): Style | Array<Style> | void {
+  protected getLineStyle(feature: FeatureLike, resolution: number): Style | Array<Style> | void {
     if (!(feature instanceof Feature)) {
       return undefined;
     }
@@ -350,34 +362,65 @@ export default class Reaction {
     feature.set('hidden', false);
 
     const styles: Array<Style> = [];
-    const maxZoom = this.mapInstance?.getView().get('originalMaxZoom');
-    const minResolution = this.mapInstance?.getView().getResolutionForZoom(maxZoom);
-    const style = feature.get('style');
-    if (!minResolution || !style) {
-      return [];
-    }
-
-    const scale = minResolution / resolution;
-    const lineWidth = feature.get('lineWidth');
+    const scale = this.minResolution / resolution;
     const type = feature.get('elementType');
     const fontSize = feature.get('fontSize');
+    let strokeStyle: Stroke | undefined;
 
     if (type === REACTION_ELEMENT_TYPES.OPERATOR && fontSize * scale <= 4) {
       return [];
     }
-    if (type === REACTION_ELEMENT_TYPES.ARROW && scale <= 0.08) {
+    if (type === REACTION_ELEMENT_TYPES.ARROW && scale <= 0.125) {
       return [];
     }
 
+    this.lineStyles.forEach(style => {
+      const styleGeometry = style.getGeometry();
+      if (styleGeometry instanceof Polygon || styleGeometry instanceof LineString) {
+        strokeStyle = styleGeometry.get('strokeStyle');
+      }
+      styles.push(getScaledElementStyle(style, strokeStyle, scale));
+    });
+
+    return styles;
+  }
+
+  protected getReactionObjectStyle(
+    feature: FeatureLike,
+    resolution: number,
+  ): Style | Array<Style> | void {
+    if (!(feature instanceof Feature)) {
+      return undefined;
+    }
+    if (this.isAnyOfElementsHidden()) {
+      feature.set('hidden', true);
+      return undefined;
+    }
+    feature.set('hidden', false);
+
+    const styles: Array<Style> = [];
+    const style = feature.get('style');
+    const scale = this.minResolution / resolution;
+    let strokeStyle: Stroke | undefined;
+
+    if (scale <= 0.125) {
+      return [];
+    }
+
+    const stylesToProcess: Array<Style> = [];
     if (style instanceof Style) {
-      styles.push(getScaledElementStyle(style, lineWidth, scale));
+      stylesToProcess.push(style);
     } else if (Array.isArray(style)) {
-      style.forEach(singleStyle => {
-        if (singleStyle instanceof Style) {
-          styles.push(getScaledElementStyle(singleStyle, lineWidth, scale));
-        }
-      });
+      stylesToProcess.push(...style);
     }
+    stylesToProcess.forEach(singleStyle => {
+      const styleGeometry = singleStyle.getGeometry();
+      if (styleGeometry instanceof Polygon || styleGeometry instanceof LineString) {
+        strokeStyle = styleGeometry.get('strokeStyle');
+      }
+      styles.push(getScaledElementStyle(singleStyle, strokeStyle, scale));
+    });
+
     return styles;
   }
 }
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getScaledElementStyle.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getScaledElementStyle.ts
index f4a4f77a..27c0c515 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getScaledElementStyle.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getScaledElementStyle.ts
@@ -1,17 +1,26 @@
+/* eslint-disable no-magic-numbers */
 import Style from 'ol/style/Style';
+import { Stroke } from 'ol/style';
 
 export default function getScaledElementStyle(
   style: Style,
-  lineWidth: number,
+  strokeStyle: Stroke | undefined,
   scale: number,
 ): Style {
-  const clonedStyle = style.clone();
-  const lineDash = clonedStyle.getStroke()?.getLineDash();
-  if (lineDash) {
-    const newLineDash = lineDash.map(width => width * scale);
-    clonedStyle.getStroke()?.setLineDash(newLineDash);
+  if (strokeStyle) {
+    const lineDash = strokeStyle.getLineDash();
+    let newLineDash: Array<number> = [];
+    if (lineDash) {
+      newLineDash = lineDash.map(width => width * scale);
+    }
+    const newStrokeStyle = new Stroke({
+      color: strokeStyle.getColor(),
+      width: (strokeStyle.getWidth() || 1) * scale,
+      lineDash: newLineDash,
+    });
+
+    style.setStroke(newStrokeStyle);
   }
-  clonedStyle.getStroke()?.setWidth(lineWidth * scale);
-  clonedStyle.getText()?.setScale(scale);
-  return clonedStyle;
+  style.getText()?.setScale(scale);
+  return style;
 }
diff --git a/src/components/Map/MapViewer/utils/useOlMap.ts b/src/components/Map/MapViewer/utils/useOlMap.ts
index 68ec65bd..23d585e0 100644
--- a/src/components/Map/MapViewer/utils/useOlMap.ts
+++ b/src/components/Map/MapViewer/utils/useOlMap.ts
@@ -65,7 +65,7 @@ export const useOlMap: UseOlMap = ({ target } = {}) => {
         mouseWheelZoom: false,
       }).extend([
         new MouseWheelZoom({
-          duration: 250,
+          duration: 0,
           timeout: 80,
         }),
       ]),
-- 
GitLab