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';
Miłosz Grocholewski
committed
import { getLayersForModel } from '@/redux/layers/layers.thunks';
import {
layersActiveLayerSelector,
Miłosz Grocholewski
committed
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';
Miłosz Grocholewski
committed
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);
Miłosz Grocholewski
committed
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);
Miłosz Grocholewski
committed
const lineTypes = useSelector(lineTypesSelector);
const arrowTypes = useSelector(arrowTypesSelector);
const pointToProjection = usePointToProjection();
const { lastJsonMessage } = useWebSocketEntityUpdatesContext();
useEffect(() => {
if (!lastJsonMessage || !('entityType' in lastJsonMessage)) {
return;
}
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]);
Miłosz Grocholewski
committed
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,
images: layer.images,
visible: layer.details.visible,
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');
Miłosz Grocholewski
committed
if (layerId && layersVisibilityForCurrentModel[layerId] !== undefined) {
layer.setVisible(layersVisibilityForCurrentModel[layerId]);
Miłosz Grocholewski
committed
}, [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]);