Skip to content
Snippets Groups Projects
useOlMapAdditionalLayers.ts 8.16 KiB
Newer Older
/* eslint-disable no-magic-numbers */
import { Collection, Feature } from 'ol';
import VectorLayer from 'ol/layer/Vector';
import VectorSource from 'ol/source/Vector';
import { useEffect, useMemo, useState } from 'react';
import { useSelector } from 'react-redux';
import { currentModelIdSelector } from '@/redux/models/models.selectors';
import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { getLayersForModel } from '@/redux/layers/layers.thunks';
import {
  layersActiveLayerSelector,
  layersForCurrentModelSelector,
  layersLoadingSelector,
  layersVisibilityForCurrentModelSelector,
} from '@/redux/layers/layers.selectors';
import { usePointToProjection } from '@/utils/map/usePointToProjection';
import { MapInstance } from '@/types/map';
import { Geometry, LineString, MultiPolygon, Point } from 'ol/geom';
import Polygon from 'ol/geom/Polygon';
import Layer from '@/components/Map/MapViewer/utils/shapes/layer/Layer';
import { arrowTypesSelector, lineTypesSelector } from '@/redux/shapes/shapes.selectors';
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import { mapDataSizeSelector } from '@/redux/map/map.selectors';
import { LayerState } from '@/redux/layers/layers.types';
import { mapEditToolsActiveActionSelector } from '@/redux/mapEditTools/mapEditTools.selectors';
import { MAP_EDIT_ACTIONS } from '@/redux/mapEditTools/mapEditTools.constants';
import getDrawBoundingBoxInteraction from '@/components/Map/MapViewer/utils/shapes/layer/interaction/getDrawBoundingBoxInteraction';
import {
  openLayerImageObjectFactoryModal,
  openLayerTextFactoryModal,
} from '@/redux/modal/modal.slice';
import { Extent } from 'ol/extent';
import { mapEditToolsSetLayerObject } from '@/redux/mapEditTools/mapEditTools.slice';
import getTransformImageInteraction from '@/components/Map/MapViewer/utils/shapes/layer/interaction/getTransformImageInteraction';
import { useWebSocketEntityUpdatesContext } from '@/utils/websocket-entity-updates/webSocketEntityUpdatesProvider';
import processMessage from '@/components/Map/MapViewer/utils/websocket/processMessage';

export const useOlMapAdditionalLayers = (
  mapInstance: MapInstance,
): Array<
  VectorLayer<
    VectorSource<Feature<Point> | Feature<Polygon> | Feature<LineString> | Feature<MultiPolygon>>
  >
> => {
  const activeAction = useAppSelector(mapEditToolsActiveActionSelector);
  const dispatch = useAppDispatch();
  const mapSize = useSelector(mapDataSizeSelector);
  const currentModelId = useSelector(currentModelIdSelector);

  const layersForCurrentModel = useAppSelector(layersForCurrentModelSelector);
  const layersLoading = useAppSelector(layersLoadingSelector);
  const layersVisibilityForCurrentModel = useAppSelector(layersVisibilityForCurrentModelSelector);
  const activeLayer = useAppSelector(layersActiveLayerSelector);

  const [layersState, setLayersState] = useState<Array<LayerState>>([]);
  const [layersLoadingState, setLayersLoadingState] = useState(false);
  const lineTypes = useSelector(lineTypesSelector);
  const arrowTypes = useSelector(arrowTypesSelector);
  const pointToProjection = usePointToProjection();

  const { lastJsonMessage } = useWebSocketEntityUpdatesContext();
    if (!lastJsonMessage || !('entityType' in lastJsonMessage)) {
    processMessage({ jsonMessage: lastJsonMessage, mapInstance });
  }, [lastJsonMessage, mapInstance]);
  const restrictionExtent: Extent = useMemo(() => {
    const restrictionMinPoint = pointToProjection({ x: 0, y: 0 });
    const restrictionMaxPoint = pointToProjection({ x: mapSize.width, y: mapSize.height });
    return [
      restrictionMinPoint[0],
      restrictionMaxPoint[1],
      restrictionMaxPoint[0],
      restrictionMinPoint[1],
    ];
  }, [mapSize, pointToProjection]);

  const drawImageInteraction = useMemo(() => {
    if (!mapSize || !dispatch) {
      return null;
    }
    return getDrawBoundingBoxInteraction(
      mapSize,
      dispatch,
      restrictionExtent,
      openLayerImageObjectFactoryModal,
    );
  }, [mapSize, dispatch, restrictionExtent]);

  const addTextInteraction = useMemo(() => {
    if (!mapSize || !dispatch) {
      return null;
    }
    return getDrawBoundingBoxInteraction(
      mapSize,
      dispatch,
      restrictionExtent,
      openLayerTextFactoryModal,
    );
  }, [mapSize, dispatch, restrictionExtent]);

  useEffect(() => {
    if (!currentModelId) {
      return;
    }
    if (!['succeeded', 'pending'].includes(layersLoading)) {
      dispatch(getLayersForModel(currentModelId));
    }
  }, [currentModelId, dispatch, layersLoading]);

  const vectorLayers = useMemo(() => {
    return layersState.map(layer => {
      const additionalLayer = new Layer({
        texts: layer.texts,
        rects: layer.rects,
        ovals: layer.ovals,
        lines: layer.lines,
        visible: layer.details.visible,
Miłosz Grocholewski's avatar
Miłosz Grocholewski committed
        layerId: layer.details.id,
        lineTypes,
        arrowTypes,
        mapInstance,
        pointToProjection,
      });
      return additionalLayer.vectorLayer;
    });
  }, [layersState, lineTypes, arrowTypes, mapInstance, mapSize, pointToProjection]);

  useEffect(() => {
    if (layersLoading === 'pending') {
      setLayersLoadingState(true);
    } else if (layersLoading === 'succeeded' && layersLoadingState) {
      setLayersLoadingState(false);
      setLayersState(layersForCurrentModel);
    }
  }, [layersForCurrentModel, layersLoading, layersLoadingState]);
  const transformInteraction = useMemo(() => {
    if (!dispatch || !currentModelId || !activeLayer) {
      return null;
    }
    let imagesFeatures: Collection<Feature<Geometry>> = new Collection();
    const vectorLayer = vectorLayers.find(layer => layer.get('id') === activeLayer);
    if (vectorLayer) {
      imagesFeatures = new Collection(vectorLayer.get('imagesFeatures'));
    }
    return getTransformImageInteraction(
      dispatch,
      mapSize,
      currentModelId,
      activeLayer,
      imagesFeatures,
      restrictionExtent,
    );
  }, [dispatch, mapSize, currentModelId, restrictionExtent, activeLayer, vectorLayers]);
  useEffect(() => {
    vectorLayers.forEach(layer => {
      const layerId = layer.get('id');
      if (layerId && layersVisibilityForCurrentModel[layerId] !== undefined) {
        layer.setVisible(layersVisibilityForCurrentModel[layerId]);
  }, [layersVisibilityForCurrentModel, vectorLayers]);
  useEffect(() => {
    const activeVectorLayer = vectorLayers.find(layer => {
      const layerId = layer.get('id');
      return layerId === activeLayer;
    });
    if (!activeVectorLayer) {
      return () => {};
    }
    const removeFeatureHandler = (): void => {
      transformInteraction?.setSelection(new Collection<Feature>());
    };

    const source = activeVectorLayer.getSource();
    source?.on('removefeature', removeFeatureHandler);

    return () => {
      if (source) {
        source.un('removefeature', removeFeatureHandler);
      }
    };
  }, [activeLayer, layersVisibilityForCurrentModel, transformInteraction, vectorLayers]);

  useEffect(() => {
    if (!transformInteraction) {
      return () => {};
    }
    if (!activeLayer || activeAction !== MAP_EDIT_ACTIONS.TRANSFORM_IMAGE) {
      return () => {};
    }
    mapInstance?.addInteraction(transformInteraction);
    return () => {
      dispatch(mapEditToolsSetLayerObject(null));
      mapInstance?.removeInteraction(transformInteraction);
    };
  }, [activeAction, activeLayer, dispatch, mapInstance, transformInteraction]);
  useEffect(() => {
    if (!drawImageInteraction) {
      return;
    }
    mapInstance?.removeInteraction(drawImageInteraction);
    if (!activeLayer || activeAction !== MAP_EDIT_ACTIONS.DRAW_IMAGE) {
      return;
    }
    mapInstance?.addInteraction(drawImageInteraction);
  }, [activeAction, activeLayer, currentModelId, drawImageInteraction, mapInstance]);
  useEffect(() => {
    if (!addTextInteraction) {
      return;
    }
    mapInstance?.removeInteraction(addTextInteraction);
    if (!activeLayer || activeAction !== MAP_EDIT_ACTIONS.ADD_TEXT) {
      return;
    }
    mapInstance?.addInteraction(addTextInteraction);
  }, [activeAction, activeLayer, currentModelId, addTextInteraction, mapInstance]);
  return vectorLayers;
};