diff --git a/frontend/src/features/InterestPoint/layers/InterestPointLayer.jsx b/frontend/src/features/InterestPoint/layers/InterestPointLayer.jsx
deleted file mode 100644
index b4c1a7ce32..0000000000
--- a/frontend/src/features/InterestPoint/layers/InterestPointLayer.jsx
+++ /dev/null
@@ -1,373 +0,0 @@
-import { usePrevious } from '@mtes-mct/monitor-ui'
-import { useEffect, useRef, useState } from 'react'
-
-import { useDispatch, useSelector } from 'react-redux'
-import VectorSource from 'ol/source/Vector'
-import { MapBox, OPENLAYERS_PROJECTION } from '../../../domain/entities/map/constants'
-import Draw from 'ol/interaction/Draw'
-import VectorLayer from 'ol/layer/Vector'
-import { getInterestPointStyle, POIStyle } from './interestPoint.style'
-import { v4 as uuidv4 } from 'uuid'
-import { InterestPointOverlay } from '../components/InterestPointOverlay'
-import {
- deleteInterestPointBeingDrawed,
- editInterestPoint,
- endInterestPointDraw,
- removeInterestPoint,
- resetInterestPointFeatureDeletion,
- updateInterestPointBeingDrawed,
- updateInterestPointKeyBeingDrawed
-} from '../slice'
-import {
- coordinatesAreModified,
- coordinatesOrTypeAreModified,
- InterestPointType
-} from '../utils'
-import saveInterestPointFeature from '../../../domain/use_cases/interestPoint/saveInterestPointFeature'
-import GeoJSON from 'ol/format/GeoJSON'
-import LineString from 'ol/geom/LineString'
-import { InterestPointLine } from './interestPointLine'
-import { getLength } from 'ol/sphere'
-import { LayerProperties } from '../../../domain/entities/layers/constants'
-import { setRightMapBoxOpened } from '../../../domain/shared_slices/Global'
-import { monitorfishMap } from '../../map/monitorfishMap'
-
-const DRAW_START_EVENT = 'drawstart'
-const DRAW_ABORT_EVENT = 'drawabort'
-const DRAW_END_EVENT = 'drawend'
-
-export const MIN_ZOOM = 7
-
-const InterestPointLayer = ({ mapMovingAndZoomEvent }) => {
- const dispatch = useDispatch()
-
- const {
- isDrawing,
- isEditing,
- /** @type {InterestPoint | null} interestPointBeingDrawed */
- interestPointBeingDrawed,
- /** @type {InterestPoint[]} interestPoints */
- interestPoints,
- triggerInterestPointFeatureDeletion
- } = useSelector(state => state.interestPoint)
-
- const [drawObject, setDrawObject] = useState(null)
-
- const vectorSourceRef = useRef(null)
- function getVectorSource () {
- if (vectorSourceRef.current === null) {
- vectorSourceRef.current = new VectorSource({
- wrapX: false,
- projection: OPENLAYERS_PROJECTION
- })
- }
- return vectorSourceRef.current
- }
-
- const layerRef = useRef(null)
- function getLayer () {
- if (layerRef.current === null) {
- layerRef.current = new VectorLayer({
- source: getVectorSource(),
- renderBuffer: 7,
- updateWhileAnimating: true,
- updateWhileInteracting: true,
- style: (feature, resolution) => getInterestPointStyle(feature, resolution),
- zIndex: LayerProperties.INTEREST_POINT.zIndex
- })
- }
- return layerRef.current
- }
-
- const previousMapZoom = useRef('')
- const [interestPointToCoordinates, setInterestPointToCoordinates] = useState(new Map())
- const previousInterestPointBeingDrawed = usePrevious(interestPointBeingDrawed)
-
- useEffect(() => {
- function addLayerToMap () {
- monitorfishMap.getLayers().push(getLayer())
-
- return () => {
- monitorfishMap.removeLayer(getLayer())
- }
- }
-
- addLayerToMap()
- }, [])
-
- useEffect(() => {
- function drawExistingFeaturesOnMap () {
- if (interestPoints) {
- const features = interestPoints.map(interestPoint => {
- if (interestPoint.feature) {
- const nextFeature = new GeoJSON({
- featureProjection: OPENLAYERS_PROJECTION
- }).readFeature(interestPoint.feature)
-
- const { feature, ...interestPointWithoutFeature } = interestPoint
- nextFeature.setProperties(interestPointWithoutFeature)
-
- return nextFeature
- }
-
- return null
- }).filter(feature => feature)
-
- getVectorSource().addFeatures(features)
- }
- }
-
- drawExistingFeaturesOnMap()
- }, [interestPoints])
-
- useEffect(() => {
- if (isDrawing) {
- function addEmptyNextInterestPoint () {
- dispatch(updateInterestPointBeingDrawed({
- uuid: uuidv4(),
- name: null,
- type: InterestPointType.FISHING_VESSEL,
- coordinates: null,
- observations: null
- }))
- }
-
- function drawNewFeatureOnMap () {
- const draw = new Draw({
- source: getVectorSource(),
- type: 'Point',
- style: POIStyle
- })
-
- monitorfishMap.addInteraction(draw)
- setDrawObject(draw)
- }
-
- addEmptyNextInterestPoint()
- drawNewFeatureOnMap()
- }
- }, [isDrawing])
-
- useEffect(() => {
- function removeInteraction () {
- if (!isDrawing && drawObject) {
- setDrawObject(null)
-
- function waitForUnwantedZoomAndQuitInteraction () {
- setTimeout(() => {
- monitorfishMap.removeInteraction(drawObject)
- }, 300)
- }
-
- waitForUnwantedZoomAndQuitInteraction()
- }
- }
-
- removeInteraction()
- }, [isDrawing])
-
- useEffect(() => {
- function handleDrawEvents () {
- if (drawObject) {
- drawObject.once(DRAW_START_EVENT, event => {
- function startDrawing (event, type) {
- dispatch(updateInterestPointBeingDrawed({
- uuid: interestPointBeingDrawed.uuid,
- name: null,
- type: type,
- coordinates: event.feature.getGeometry().getLastCoordinate(),
- observations: null
- }))
- }
-
- if (interestPointBeingDrawed) {
- startDrawing(event, interestPointBeingDrawed.type || InterestPointType.OTHER)
- }
- })
-
- drawObject.once(DRAW_ABORT_EVENT, () => {
- dispatch(endInterestPointDraw())
- dispatch(deleteInterestPointBeingDrawed())
- dispatch(setRightMapBoxOpened(undefined))
- })
-
- drawObject.once(DRAW_END_EVENT, event => {
- dispatch(saveInterestPointFeature(event.feature))
- dispatch(endInterestPointDraw())
- })
- }
- }
-
- handleDrawEvents()
- }, [drawObject, interestPointBeingDrawed])
-
- useEffect(() => {
- function showOrHideInterestPointsOverlays () {
- const currentZoom = monitorfishMap.getView().getZoom().toFixed(2)
- if (currentZoom !== previousMapZoom.current) {
- previousMapZoom.current = currentZoom
- if (currentZoom < MIN_ZOOM) {
- getVectorSource().forEachFeature(feature => {
- feature.set(InterestPointLine.isHiddenByZoomProperty, true)
- })
- } else {
- getVectorSource().forEachFeature(feature => {
- feature.set(InterestPointLine.isHiddenByZoomProperty, false)
- })
- }
- }
- }
-
- showOrHideInterestPointsOverlays()
- }, [mapMovingAndZoomEvent])
-
- useEffect(() => {
- if (triggerInterestPointFeatureDeletion) {
- deleteInterestPoint(triggerInterestPointFeatureDeletion)
- resetInterestPointFeatureDeletion()
- }
- }, [triggerInterestPointFeatureDeletion])
-
- useEffect(() => {
- function modifyFeatureWhenCoordinatesOrTypeModified () {
- if (interestPointBeingDrawed?.coordinates?.length && interestPointBeingDrawed?.uuid) {
- const drawingFeatureToUpdate = getVectorSource().getFeatureById(interestPointBeingDrawed.uuid)
-
- if (drawingFeatureToUpdate && coordinatesOrTypeAreModified(drawingFeatureToUpdate, interestPointBeingDrawed)) {
- const { feature, ...interestPointWithoutFeature } = interestPointBeingDrawed
- drawingFeatureToUpdate.getGeometry().setCoordinates(interestPointWithoutFeature.coordinates)
- drawingFeatureToUpdate.setProperties(interestPointWithoutFeature)
-
- const nextFeature = new GeoJSON()
- .writeFeatureObject(drawingFeatureToUpdate, { featureProjection: OPENLAYERS_PROJECTION })
-
- dispatch(updateInterestPointKeyBeingDrawed({
- key: 'feature',
- value: nextFeature
- }))
- }
- }
- }
-
- modifyFeatureWhenCoordinatesOrTypeModified()
- }, [interestPointBeingDrawed?.coordinates, interestPointBeingDrawed?.type])
-
- useEffect(() => {
- function initLineWhenInterestPointCoordinatesModified () {
- if (interestPointBeingDrawed && previousInterestPointBeingDrawed && coordinatesAreModified(interestPointBeingDrawed, previousInterestPointBeingDrawed)) {
- const line = new LineString([interestPointBeingDrawed.coordinates, previousInterestPointBeingDrawed.coordinates])
- const distance = getLength(line, { projection: OPENLAYERS_PROJECTION })
-
- if (distance > 10) {
- const featureId = InterestPointLine.getFeatureId(interestPointBeingDrawed.uuid)
- if (interestPointToCoordinates.has(featureId)) {
- interestPointToCoordinates.delete(featureId)
- const feature = getVectorSource().getFeatureById(featureId)
- if (feature) {
- feature.setGeometry(new LineString([interestPointBeingDrawed.coordinates, interestPointBeingDrawed.coordinates]))
- }
- }
- }
- }
- }
-
- initLineWhenInterestPointCoordinatesModified()
- }, [interestPointBeingDrawed])
-
- function deleteInterestPoint (uuid) {
- const feature = getVectorSource().getFeatureById(uuid)
- if (feature) {
- getVectorSource().removeFeature(feature)
- getVectorSource().changed()
- }
-
- const featureLine = getVectorSource().getFeatureById(InterestPointLine.getFeatureId(uuid))
- if (featureLine) {
- getVectorSource().removeFeature(featureLine)
- getVectorSource().changed()
- }
-
- dispatch(removeInterestPoint(uuid))
- }
-
- function moveInterestPointLine (uuid, coordinates, nextCoordinates, offset) {
- const featureId = InterestPointLine.getFeatureId(uuid)
-
- if (interestPointToCoordinates.has(featureId)) {
- const existingLabelLineFeature = getVectorSource().getFeatureById(featureId)
- const interestPointFeature = getVectorSource().getFeatureById(uuid)
-
- if (existingLabelLineFeature) {
- if (interestPointFeature) {
- existingLabelLineFeature.setGeometry(new LineString([interestPointFeature.getGeometry().getCoordinates(), nextCoordinates]))
- }
- }
- } else {
- const interestPointLineFeature = InterestPointLine.getFeature(
- coordinates,
- nextCoordinates,
- featureId)
-
- getVectorSource().addFeature(interestPointLineFeature)
- }
-
- const nextVesselToCoordinates = interestPointToCoordinates
- interestPointToCoordinates.set(featureId, { coordinates: nextCoordinates, offset })
- setInterestPointToCoordinates(nextVesselToCoordinates)
- }
-
- function modifyInterestPoint (uuid) {
- dispatch(editInterestPoint(uuid))
- dispatch(setRightMapBoxOpened(MapBox.INTEREST_POINT))
- }
-
- function deleteInterestPointBeingDrawedAndCloseTool () {
- dispatch(endInterestPointDraw())
- dispatch(setRightMapBoxOpened(undefined))
- dispatch(deleteInterestPointBeingDrawed())
- }
-
- return (
- <>
-
- {
- interestPoints && Array.isArray(interestPoints)
- ? interestPoints.map(interestPoint => {
- return
- })
- : null
- }
- {
- interestPointBeingDrawed && !isEditing
- ? {}}
- zoomHasChanged={previousMapZoom.current}
- moveLine={moveInterestPointLine}
- />
- : null
- }
-
- >
- )
-}
-
-export default InterestPointLayer
diff --git a/frontend/src/features/InterestPoint/layers/InterestPointLayer.tsx b/frontend/src/features/InterestPoint/layers/InterestPointLayer.tsx
new file mode 100644
index 0000000000..112b110dd2
--- /dev/null
+++ b/frontend/src/features/InterestPoint/layers/InterestPointLayer.tsx
@@ -0,0 +1,395 @@
+import { useMainAppDispatch } from '@hooks/useMainAppDispatch'
+import { useMainAppSelector } from '@hooks/useMainAppSelector'
+import { usePrevious } from '@mtes-mct/monitor-ui'
+import { omit } from 'lodash'
+import GeoJSON from 'ol/format/GeoJSON'
+import LineString from 'ol/geom/LineString'
+import Draw from 'ol/interaction/Draw'
+import VectorLayer from 'ol/layer/Vector'
+import VectorSource from 'ol/source/Vector'
+import { getLength } from 'ol/sphere'
+import { useCallback, useEffect, useRef, useState } from 'react'
+import { v4 as uuidv4 } from 'uuid'
+
+import { getInterestPointStyle, POIStyle } from './interestPoint.style'
+import { InterestPointLine } from './InterestPointLine'
+import { LayerProperties } from '../../../domain/entities/layers/constants'
+import { MapBox, OPENLAYERS_PROJECTION } from '../../../domain/entities/map/constants'
+import { setRightMapBoxOpened } from '../../../domain/shared_slices/Global'
+import saveInterestPointFeature from '../../../domain/use_cases/interestPoint/saveInterestPointFeature'
+import { monitorfishMap } from '../../map/monitorfishMap'
+import { InterestPointOverlay } from '../components/InterestPointOverlay'
+import {
+ deleteInterestPointBeingDrawed,
+ editInterestPoint,
+ endInterestPointDraw,
+ removeInterestPoint,
+ resetInterestPointFeatureDeletion,
+ updateInterestPointBeingDrawed,
+ updateInterestPointKeyBeingDrawed
+} from '../slice'
+import { coordinatesAreModified, coordinatesOrTypeAreModified, InterestPointType } from '../utils'
+
+import type { DummyObjectToForceEffectHookUpdate } from '@features/map/types'
+import type { Feature } from 'ol'
+import type { Geometry } from 'ol/geom'
+
+const DRAW_START_EVENT = 'drawstart'
+const DRAW_ABORT_EVENT = 'drawabort'
+const DRAW_END_EVENT = 'drawend'
+
+export const MIN_ZOOM = 7
+
+type InterstPointLayerProps = Readonly<{
+ mapMovingAndZoomEvent: DummyObjectToForceEffectHookUpdate
+}>
+export function InterestPointLayer({ mapMovingAndZoomEvent }: InterstPointLayerProps) {
+ const previousMapZoom = useRef('')
+ const vectorSourceRef = useRef>>(
+ new VectorSource({
+ // TODO Fix that: `projection` propr does not exist in type `Options>`.
+ // @ts-ignore
+ projection: OPENLAYERS_PROJECTION,
+ wrapX: false
+ })
+ )
+ const layerRef = useRef>>(
+ new VectorLayer({
+ renderBuffer: 7,
+ source: vectorSourceRef.current,
+ style: (feature, resolution) => getInterestPointStyle(feature, resolution),
+ updateWhileAnimating: true,
+ updateWhileInteracting: true,
+ zIndex: LayerProperties.INTEREST_POINT.zIndex
+ })
+ )
+
+ const dispatch = useMainAppDispatch()
+ const {
+ interestPointBeingDrawed,
+ interestPoints,
+ /** @type {InterestPoint | null} interestPointBeingDrawed */
+ isDrawing,
+ /** @type {InterestPoint[]} interestPoints */
+ isEditing,
+ triggerInterestPointFeatureDeletion
+ } = useMainAppSelector(state => state.interestPoint)
+
+ const [drawObject, setDrawObject] = useState(null)
+ const [interestPointToCoordinates, setInterestPointToCoordinates] = useState(new Map())
+ const previousInterestPointBeingDrawed = usePrevious(interestPointBeingDrawed)
+
+ const addEmptyNextInterestPoint = useCallback(() => {
+ dispatch(
+ updateInterestPointBeingDrawed({
+ coordinates: null,
+ name: null,
+ observations: null,
+ type: InterestPointType.FISHING_VESSEL,
+ uuid: uuidv4()
+ })
+ )
+ }, [dispatch])
+
+ const drawNewFeatureOnMap = useCallback(() => {
+ const draw = new Draw({
+ source: vectorSourceRef.current,
+ style: POIStyle,
+ type: 'Point'
+ })
+
+ monitorfishMap.addInteraction(draw)
+ setDrawObject(draw)
+ }, [])
+
+ const waitForUnwantedZoomAndQuitInteraction = useCallback(() => {
+ setTimeout(() => {
+ if (drawObject) {
+ monitorfishMap.removeInteraction(drawObject)
+ }
+ }, 300)
+ }, [drawObject])
+
+ const startDrawing = useCallback(
+ (event, type) => {
+ dispatch(
+ updateInterestPointBeingDrawed({
+ coordinates: event.feature.getGeometry().getLastCoordinate(),
+ name: null,
+ observations: null,
+ type,
+ uuid: interestPointBeingDrawed?.uuid
+ })
+ )
+ },
+ [dispatch, interestPointBeingDrawed]
+ )
+
+ const handleDrawEvents = useCallback(() => {
+ if (drawObject) {
+ drawObject.once(DRAW_START_EVENT, event => {
+ if (interestPointBeingDrawed) {
+ startDrawing(event, interestPointBeingDrawed.type || InterestPointType.OTHER)
+ }
+ })
+
+ drawObject.once(DRAW_ABORT_EVENT, () => {
+ dispatch(endInterestPointDraw())
+ dispatch(deleteInterestPointBeingDrawed())
+ dispatch(setRightMapBoxOpened(undefined))
+ })
+
+ drawObject.once(DRAW_END_EVENT, event => {
+ dispatch(saveInterestPointFeature(event.feature))
+ dispatch(endInterestPointDraw())
+ })
+ }
+ }, [dispatch, drawObject, interestPointBeingDrawed, startDrawing])
+
+ const showOrHideInterestPointsOverlays = useCallback(() => {
+ const currentZoom = monitorfishMap.getView().getZoom()?.toFixed(2)
+ if (!!currentZoom && currentZoom !== previousMapZoom.current) {
+ previousMapZoom.current = currentZoom
+ if (Number(currentZoom) < MIN_ZOOM) {
+ vectorSourceRef.current.forEachFeature(feature => {
+ feature.set(InterestPointLine.isHiddenByZoomProperty, true)
+ })
+ } else {
+ vectorSourceRef.current.forEachFeature(feature => {
+ feature.set(InterestPointLine.isHiddenByZoomProperty, false)
+ })
+ }
+ }
+ }, [])
+
+ const deleteInterestPoint = useCallback(
+ (uuid: string) => {
+ const feature = vectorSourceRef.current.getFeatureById(uuid)
+ if (feature) {
+ vectorSourceRef.current.removeFeature(feature)
+ vectorSourceRef.current.changed()
+ }
+
+ const featureLine = vectorSourceRef.current.getFeatureById(InterestPointLine.getFeatureId(uuid))
+ if (featureLine) {
+ vectorSourceRef.current.removeFeature(featureLine)
+ vectorSourceRef.current.changed()
+ }
+
+ dispatch(removeInterestPoint(uuid))
+ },
+ [dispatch]
+ )
+
+ const moveInterestPointLine = useCallback(
+ (uuid: string, coordinates: string[], nextCoordinates: string[], offset: number) => {
+ const featureId = InterestPointLine.getFeatureId(uuid)
+
+ if (interestPointToCoordinates.has(featureId)) {
+ const existingLabelLineFeature = vectorSourceRef.current.getFeatureById(featureId)
+ const interestPointFeature = vectorSourceRef.current.getFeatureById(uuid)
+
+ if (existingLabelLineFeature) {
+ if (interestPointFeature) {
+ const geometry = existingLabelLineFeature.getGeometry()
+ // TODO Fix that. There may be a real bug here: `Property 'getCoordinates' does not exist on type 'Geometry'.`. Legacy code.
+ // @ts-ignore
+ const previousCoordinates = geometry?.getCoordinates()
+
+ existingLabelLineFeature.setGeometry(new LineString([previousCoordinates, nextCoordinates]))
+ }
+ }
+ } else {
+ const interestPointLineFeature = InterestPointLine.getFeature(coordinates, nextCoordinates, featureId)
+
+ vectorSourceRef.current.addFeature(interestPointLineFeature)
+ }
+
+ const nextVesselToCoordinates = interestPointToCoordinates
+ interestPointToCoordinates.set(featureId, { coordinates: nextCoordinates, offset })
+ setInterestPointToCoordinates(nextVesselToCoordinates)
+ },
+ [interestPointToCoordinates]
+ )
+
+ const modifyInterestPoint = useCallback(
+ (uuid: string) => {
+ dispatch(editInterestPoint(uuid))
+ dispatch(setRightMapBoxOpened(MapBox.INTEREST_POINT))
+ },
+ [dispatch]
+ )
+
+ const deleteInterestPointBeingDrawedAndCloseTool = useCallback(() => {
+ dispatch(endInterestPointDraw())
+ dispatch(setRightMapBoxOpened(undefined))
+ dispatch(deleteInterestPointBeingDrawed())
+ }, [dispatch])
+
+ const modifyFeatureWhenCoordinatesOrTypeModified = useCallback(() => {
+ if (interestPointBeingDrawed?.coordinates?.length && interestPointBeingDrawed?.uuid) {
+ const drawingFeatureToUpdate = vectorSourceRef.current.getFeatureById(interestPointBeingDrawed.uuid)
+
+ if (drawingFeatureToUpdate && coordinatesOrTypeAreModified(drawingFeatureToUpdate, interestPointBeingDrawed)) {
+ const interestPointWithoutFeature = omit(interestPointBeingDrawed, 'feature')
+ const drawingFeatureToUpdateGeometry = drawingFeatureToUpdate.getGeometry()
+ if (drawingFeatureToUpdateGeometry) {
+ // TODO Fix that. There may be a real bug here: `Property 'setCoordinates' does not exist on type 'Geometry'.`. Legacy code.
+ // @ts-ignore
+ drawingFeatureToUpdateGeometry.setCoordinates(interestPointBeingDrawed.coordinates)
+ }
+ drawingFeatureToUpdate.setProperties(interestPointWithoutFeature)
+
+ const nextFeature = new GeoJSON().writeFeatureObject(drawingFeatureToUpdate, {
+ featureProjection: OPENLAYERS_PROJECTION
+ })
+
+ dispatch(
+ updateInterestPointKeyBeingDrawed({
+ key: 'feature',
+ value: nextFeature
+ })
+ )
+ }
+ }
+ }, [dispatch, interestPointBeingDrawed])
+
+ const initLineWhenInterestPointCoordinatesModified = useCallback(() => {
+ if (
+ interestPointBeingDrawed &&
+ previousInterestPointBeingDrawed &&
+ coordinatesAreModified(interestPointBeingDrawed, previousInterestPointBeingDrawed)
+ ) {
+ const line = new LineString([interestPointBeingDrawed.coordinates, previousInterestPointBeingDrawed.coordinates])
+ const distance = getLength(line, { projection: OPENLAYERS_PROJECTION })
+
+ if (distance > 10) {
+ const featureId = InterestPointLine.getFeatureId(interestPointBeingDrawed.uuid)
+ if (interestPointToCoordinates.has(featureId)) {
+ interestPointToCoordinates.delete(featureId)
+ const feature = vectorSourceRef.current.getFeatureById(featureId)
+ if (feature) {
+ feature.setGeometry(
+ new LineString([interestPointBeingDrawed.coordinates, interestPointBeingDrawed.coordinates])
+ )
+ }
+ }
+ }
+ }
+ }, [interestPointBeingDrawed, interestPointToCoordinates, previousInterestPointBeingDrawed])
+
+ useEffect(() => {
+ const layer = layerRef.current
+
+ monitorfishMap.getLayers().push(layer)
+
+ return () => {
+ monitorfishMap.removeLayer(layer)
+ }
+ }, [])
+
+ useEffect(() => {
+ function drawExistingFeaturesOnMap() {
+ if (interestPoints) {
+ const features = interestPoints
+ .map(interestPoint => {
+ if (interestPoint.feature) {
+ const nextFeature = new GeoJSON({
+ featureProjection: OPENLAYERS_PROJECTION
+ }).readFeature(interestPoint.feature)
+
+ const interestPointWithoutFeature = omit(interestPoint, 'feature')
+ nextFeature.setProperties(interestPointWithoutFeature)
+
+ return nextFeature
+ }
+
+ return null
+ })
+ .filter(feature => !!feature)
+
+ vectorSourceRef.current.addFeatures(features)
+ }
+ }
+
+ drawExistingFeaturesOnMap()
+ }, [interestPoints])
+
+ useEffect(() => {
+ if (isDrawing) {
+ addEmptyNextInterestPoint()
+ drawNewFeatureOnMap()
+ }
+ }, [addEmptyNextInterestPoint, drawNewFeatureOnMap, isDrawing])
+
+ useEffect(() => {
+ function removeInteraction() {
+ if (!isDrawing && drawObject) {
+ setDrawObject(null)
+
+ waitForUnwantedZoomAndQuitInteraction()
+ }
+ }
+
+ removeInteraction()
+ }, [drawObject, isDrawing, waitForUnwantedZoomAndQuitInteraction])
+
+ useEffect(() => {
+ handleDrawEvents()
+ }, [handleDrawEvents])
+
+ useEffect(() => {
+ showOrHideInterestPointsOverlays()
+ }, [mapMovingAndZoomEvent, showOrHideInterestPointsOverlays])
+
+ useEffect(() => {
+ if (triggerInterestPointFeatureDeletion) {
+ deleteInterestPoint(triggerInterestPointFeatureDeletion)
+ resetInterestPointFeatureDeletion()
+ }
+ }, [deleteInterestPoint, triggerInterestPointFeatureDeletion])
+
+ useEffect(() => {
+ modifyFeatureWhenCoordinatesOrTypeModified()
+ }, [modifyFeatureWhenCoordinatesOrTypeModified])
+
+ useEffect(() => {
+ initLineWhenInterestPointCoordinatesModified()
+ }, [initLineWhenInterestPointCoordinatesModified])
+
+ return (
+ <>
+
+ {!!interestPoints &&
+ Array.isArray(interestPoints) &&
+ interestPoints.map(interestPoint => (
+
+ ))}
+ {!!interestPointBeingDrawed && !isEditing && (
+ {}}
+ moveLine={moveInterestPointLine}
+ name={interestPointBeingDrawed.name}
+ observations={interestPointBeingDrawed.observations}
+ uuid={interestPointBeingDrawed.uuid}
+ zoomHasChanged={previousMapZoom.current}
+ />
+ )}
+
+ >
+ )
+}
diff --git a/frontend/src/features/InterestPoint/layers/interestPointLine.js b/frontend/src/features/InterestPoint/layers/InterestPointLine.ts
similarity index 55%
rename from frontend/src/features/InterestPoint/layers/interestPointLine.js
rename to frontend/src/features/InterestPoint/layers/InterestPointLine.ts
index aa6374fa32..bac6ad30ed 100644
--- a/frontend/src/features/InterestPoint/layers/interestPointLine.js
+++ b/frontend/src/features/InterestPoint/layers/InterestPointLine.ts
@@ -4,14 +4,18 @@ import LineString from 'ol/geom/LineString'
export class InterestPointLine {
static typeProperty = 'type'
static isHiddenByZoomProperty = 'isHiddenByZoom'
+
/**
* InterestPointLine object for building OpenLayers interest point line to draggable overlay
- * @param {string[]} fromCoordinates - The [longitude, latitude] of the start of the line (the interest point position)
- * @param {string[]} toCoordinates - The [longitude, latitude] of the overlay position
- * @param {string} featureId - The feature identifier
+ *
+ * @param fromCoordinates - The [longitude, latitude] of the start of the line (the interest point position)
+ * @param toCoordinates - The [longitude, latitude] of the overlay position
+ * @param featureId - The feature identifier
*/
- static getFeature(fromCoordinates, toCoordinates, featureId) {
+ static getFeature(fromCoordinates: string[], toCoordinates: string[], featureId: string) {
const interestPointLineFeature = new Feature({
+ // TODO Fix that: `ype 'string[]' is not assignable to type 'number | Coordinate'.`. Legacy code.
+ // @ts-ignore
geometry: new LineString([fromCoordinates, toCoordinates])
})
diff --git a/frontend/src/features/InterestPoint/layers/interestPoint.style.tsx b/frontend/src/features/InterestPoint/layers/interestPoint.style.tsx
index f9b452852c..9f3a3ffc29 100644
--- a/frontend/src/features/InterestPoint/layers/interestPoint.style.tsx
+++ b/frontend/src/features/InterestPoint/layers/interestPoint.style.tsx
@@ -4,7 +4,7 @@ import CircleStyle from 'ol/style/Circle'
import Fill from 'ol/style/Fill'
import Stroke from 'ol/style/Stroke'
-import { InterestPointLine } from './interestPointLine'
+import { InterestPointLine } from './InterestPointLine'
import { INTEREST_POINT_STYLE, InterestPointType } from '../utils'
const interestPointStylesCache = new Map()
diff --git a/frontend/src/features/Measurement/layers/MeasurementLayer.jsx b/frontend/src/features/Measurement/layers/MeasurementLayer.jsx
deleted file mode 100644
index b7f7aaf79a..0000000000
--- a/frontend/src/features/Measurement/layers/MeasurementLayer.jsx
+++ /dev/null
@@ -1,309 +0,0 @@
-import React, { useEffect, useRef, useState } from 'react'
-
-import { useDispatch, useSelector } from 'react-redux'
-import VectorSource from 'ol/source/Vector'
-import { MeasurementType, OPENLAYERS_PROJECTION, WSG84_PROJECTION } from '../../../domain/entities/map/constants'
-import Draw from 'ol/interaction/Draw'
-import { unByKey } from 'ol/Observable'
-import LineString from 'ol/geom/LineString'
-import { getLength } from 'ol/sphere'
-import {
- removeMeasurementDrawed,
- resetMeasurementTypeToAdd,
- setCircleMeasurementInDrawing
-} from '../slice'
-import VectorLayer from 'ol/layer/Vector'
-import Circle from 'ol/geom/Circle'
-import { circular, fromCircle } from 'ol/geom/Polygon'
-import Feature from 'ol/Feature'
-import { METERS_PER_UNIT } from 'ol/proj/Units'
-import GeoJSON from 'ol/format/GeoJSON'
-import MeasurementOverlay from '../components/MeasurementOverlay'
-import { getNauticalMilesFromMeters } from '../../../utils'
-import saveMeasurement from '../../../domain/use_cases/measurement/saveMeasurement'
-import { measurementStyle, measurementStyleWithCenter } from './measurement.style'
-import { transform } from 'ol/proj'
-import { getCenter } from 'ol/extent'
-import { LayerProperties } from '../../../domain/entities/layers/constants'
-import { monitorfishMap } from '../../map/monitorfishMap'
-
-const DRAW_START_EVENT = 'drawstart'
-const DRAW_ABORT_EVENT = 'drawabort'
-const DRAW_END_EVENT = 'drawend'
-
-const getNauticalMilesRadiusOfCircle = circle => {
- const polygon = fromCircle(circle)
-
- return getNauticalMilesRadiusOfCircularPolygon(polygon)
-}
-
-const getNauticalMilesOfLine = line => {
- const length = getLength(line)
-
- return `${getNauticalMilesFromMeters(length)} nm`
-}
-
-function getNauticalMilesRadiusOfCircularPolygon (polygon) {
- const length = getLength(polygon)
- const radius = length / (2 * Math.PI)
-
- return `r = ${getNauticalMilesFromMeters(radius)} nm`
-}
-
-const MeasurementLayer = () => {
- const dispatch = useDispatch()
-
- const {
- measurementTypeToAdd,
- measurementsDrawed,
- circleMeasurementToAdd
- } = useSelector(state => state.measurement)
-
- const [measurementInProgress, _setMeasurementInProgress] = useState(null)
- const measurementInProgressRef = useRef(measurementInProgress)
- const setMeasurementInProgress = value => {
- measurementInProgressRef.current = value
- _setMeasurementInProgress(value)
- }
- const [drawObject, setDrawObject] = useState(null)
- const [vectorSource] = useState(new VectorSource({
- wrapX: false,
- projection: OPENLAYERS_PROJECTION
- }))
- const [vectorLayer] = useState(new VectorLayer({
- source: vectorSource,
- renderBuffer: 7,
- updateWhileAnimating: true,
- updateWhileInteracting: true,
- style: [measurementStyle, measurementStyleWithCenter],
- className: LayerProperties.MEASUREMENT.code,
- zIndex: LayerProperties.MEASUREMENT.zIndex
- }))
-
- useEffect(() => {
- function addLayerToMap () {
- if (vectorLayer) {
- monitorfishMap.getLayers().push(vectorLayer)
- }
-
- return () => {
- monitorfishMap.removeLayer(vectorLayer)
- }
- }
-
- addLayerToMap()
- }, [vectorLayer])
-
- useEffect(() => {
- function drawExistingFeaturesOnMap () {
- if (measurementsDrawed) {
- measurementsDrawed.forEach(measurement => {
- const feature = new GeoJSON({
- featureProjection: OPENLAYERS_PROJECTION
- }).readFeature(measurement.feature)
-
- vectorSource.addFeature(feature)
- })
- }
- }
-
- drawExistingFeaturesOnMap()
- }, [measurementsDrawed])
-
- useEffect(() => {
- if (measurementTypeToAdd) {
- function addEmptyNextMeasurement () {
- setMeasurementInProgress({
- feature: null,
- measurement: null,
- coordinates: null
- })
- }
-
- function drawNewFeatureOnMap () {
- const draw = new Draw({
- source: vectorSource,
- type: measurementTypeToAdd,
- style: [measurementStyle, measurementStyleWithCenter]
- })
-
- monitorfishMap.addInteraction(draw)
- setDrawObject(draw)
- }
-
- addEmptyNextMeasurement()
- drawNewFeatureOnMap()
- }
- }, [measurementTypeToAdd])
-
- useEffect(() => {
- function removeInteraction () {
- if (!measurementTypeToAdd && drawObject) {
- setDrawObject(null)
- setMeasurementInProgress(null)
-
- waitForUnwantedZoomAndQuitInteraction()
- }
- }
-
- function waitForUnwantedZoomAndQuitInteraction () {
- setTimeout(() => {
- monitorfishMap.removeInteraction(drawObject)
- }, 300)
- }
-
- removeInteraction()
- }, [measurementTypeToAdd])
-
- useEffect(() => {
- function addCustomCircleMeasurement () {
- const metersForOneNauticalMile = 1852
- const longitude = 1
- const latitude = 0
- const numberOfVertices = 64
-
- if (!circleMeasurementHasCoordinatesAndRadiusFromForm() && !circleMeasurementHasRadiusFromFormAndCoordinatesFromDraw()) {
- return
- }
-
- function circleMeasurementHasCoordinatesAndRadiusFromForm () {
- return circleMeasurementToAdd?.circleCoordinatesToAdd?.length === 2 && circleMeasurementToAdd?.circleRadiusToAdd
- }
-
- function circleMeasurementHasRadiusFromFormAndCoordinatesFromDraw () {
- return circleMeasurementToAdd?.circleRadiusToAdd && measurementInProgress?.center?.length === 2
- }
-
- const radiusInMeters = METERS_PER_UNIT.m * circleMeasurementToAdd.circleRadiusToAdd * metersForOneNauticalMile
- let coordinates = []
- if (circleMeasurementHasCoordinatesAndRadiusFromForm()) {
- coordinates = [circleMeasurementToAdd.circleCoordinatesToAdd[longitude], circleMeasurementToAdd.circleCoordinatesToAdd[latitude]]
- } else if (circleMeasurementHasRadiusFromFormAndCoordinatesFromDraw()) {
- coordinates = transform(measurementInProgress?.center, OPENLAYERS_PROJECTION, WSG84_PROJECTION)
- }
-
- const circleFeature = new Feature({
- geometry: circular(coordinates, radiusInMeters, numberOfVertices).transform(WSG84_PROJECTION, OPENLAYERS_PROJECTION),
- style: [measurementStyle, measurementStyleWithCenter]
- })
- dispatch(saveMeasurement(circleFeature, `r = ${circleMeasurementToAdd.circleRadiusToAdd} nm`))
- }
-
- addCustomCircleMeasurement()
- }, [circleMeasurementToAdd])
-
- useEffect(() => {
- function handleDrawEvents () {
- if (drawObject) {
- let listener
-
- drawObject.on(DRAW_START_EVENT, event => {
- listener = startDrawing(event)
- })
-
- drawObject.on(DRAW_ABORT_EVENT, () => {
- unByKey(listener)
- dispatch(resetMeasurementTypeToAdd())
- setMeasurementInProgress(null)
- })
-
- drawObject.on(DRAW_END_EVENT, event => {
- dispatch(saveMeasurement(event.feature, measurementInProgressRef.current.measurement))
-
- unByKey(listener)
- dispatch(resetMeasurementTypeToAdd())
- setMeasurementInProgress(null)
- })
- }
- }
-
- handleDrawEvents()
- }, [drawObject])
-
- useEffect(() => {
- if (measurementInProgress?.center || measurementInProgress?.measurement) {
- dispatch(setCircleMeasurementInDrawing({
- measurement: measurementInProgress.measurement,
- coordinates: measurementInProgress.center
- }))
- }
- }, [measurementInProgress])
-
- function deleteFeature (featureId) {
- const feature = vectorSource.getFeatureById(featureId)
- if (feature) {
- vectorSource.removeFeature(feature)
- vectorSource.changed()
- }
-
- dispatch(removeMeasurementDrawed(featureId))
- }
-
- function startDrawing (event) {
- const firstTooltipCoordinates = event.coordinate
-
- setMeasurementInProgress({
- measurement: 0,
- coordinates: event.feature.getGeometry().getLastCoordinate(),
- center: getCenter(event.feature.getGeometry().getExtent())
- })
-
- return event.feature.getGeometry().on('change', changeEvent => {
- function updateMeasurementOnNewPoint (event, tooltipCoordinates) {
- const geom = event.target
-
- if (geom instanceof LineString) {
- const nextMeasurementOutput = getNauticalMilesOfLine(geom)
- tooltipCoordinates = geom.getLastCoordinate()
-
- setMeasurementInProgress({
- measurement: nextMeasurementOutput,
- coordinates: tooltipCoordinates
- })
- } else if (geom instanceof Circle) {
- const nextMeasurementOutput = getNauticalMilesRadiusOfCircle(geom)
- tooltipCoordinates = geom.getLastCoordinate()
-
- setMeasurementInProgress({
- measurement: nextMeasurementOutput,
- coordinates: tooltipCoordinates,
- center: getCenter(geom.getExtent())
- })
- }
- }
-
- updateMeasurementOnNewPoint(changeEvent, firstTooltipCoordinates)
- })
- }
-
- return (
- <>
- {
- measurementsDrawed.map(measurement => {
- return
- })
- }
-
-
- {
- measurementInProgress
- ?
- : null
- }
-
- >
- )
-}
-
-export default React.memo(MeasurementLayer)
diff --git a/frontend/src/features/Measurement/layers/MeasurementLayer.tsx b/frontend/src/features/Measurement/layers/MeasurementLayer.tsx
new file mode 100644
index 0000000000..c10e4078b8
--- /dev/null
+++ b/frontend/src/features/Measurement/layers/MeasurementLayer.tsx
@@ -0,0 +1,326 @@
+import { useMainAppDispatch } from '@hooks/useMainAppDispatch'
+import { useMainAppSelector } from '@hooks/useMainAppSelector'
+import { assertNotNullish } from '@utils/assertNotNullish'
+import { noop } from 'lodash'
+import { getCenter } from 'ol/extent'
+import Feature from 'ol/Feature'
+import GeoJSON from 'ol/format/GeoJSON'
+import Circle from 'ol/geom/Circle'
+import LineString from 'ol/geom/LineString'
+import Polygon, { circular, fromCircle } from 'ol/geom/Polygon'
+import Draw, { DrawEvent } from 'ol/interaction/Draw'
+import VectorLayer from 'ol/layer/Vector'
+import { unByKey } from 'ol/Observable'
+import { transform } from 'ol/proj'
+import { METERS_PER_UNIT } from 'ol/proj/Units'
+import VectorSource from 'ol/source/Vector'
+import { getLength } from 'ol/sphere'
+import { memo, useCallback, useEffect, useRef, useState } from 'react'
+
+import { OPENLAYERS_PROJECTION, WSG84_PROJECTION } from '../../../domain/entities/map/constants'
+import saveMeasurement from '../../../domain/use_cases/measurement/saveMeasurement'
+import { getNauticalMilesFromMeters } from '../../../utils'
+import MeasurementOverlay from '../components/MeasurementOverlay'
+import { removeMeasurementDrawed, resetMeasurementTypeToAdd, setCircleMeasurementInDrawing } from '../slice'
+import { measurementStyle, measurementStyleWithCenter } from './measurement.style'
+import { LayerProperties } from '../../../domain/entities/layers/constants'
+import { monitorfishMap } from '../../map/monitorfishMap'
+
+import type { Coordinates } from '@mtes-mct/monitor-ui'
+import type { Coordinate } from 'ol/coordinate'
+
+const DRAW_START_EVENT = 'drawstart'
+const DRAW_ABORT_EVENT = 'drawabort'
+const DRAW_END_EVENT = 'drawend'
+
+const getNauticalMilesRadiusOfCircle = (circle: Circle): string => {
+ const polygon = fromCircle(circle)
+
+ return getNauticalMilesRadiusOfCircularPolygon(polygon)
+}
+
+const getNauticalMilesOfLine = (line: LineString): string => {
+ const length = getLength(line)
+
+ return `${getNauticalMilesFromMeters(length)} nm`
+}
+
+function getNauticalMilesRadiusOfCircularPolygon(polygon: Polygon): string {
+ const length = getLength(polygon)
+ const radius = length / (2 * Math.PI)
+
+ return `r = ${getNauticalMilesFromMeters(radius)} nm`
+}
+
+type MeasurementInProgress = {
+ center?: Coordinate
+ coordinates: null
+ feature?: null
+ measurement: number | string | null
+}
+
+function UnmemoizedMeasurementLayer() {
+ const vectorSourceRef = useRef(
+ new VectorSource({
+ // TODO Fix TS error `'projection' does not exist in type 'Options>'`.
+ // @ts-ignore
+ projection: OPENLAYERS_PROJECTION,
+ wrapX: false
+ })
+ )
+ const vectorLayerRef = useRef(
+ new VectorLayer({
+ className: LayerProperties.MEASUREMENT.code,
+ renderBuffer: 7,
+ source: vectorSourceRef.current,
+ style: [measurementStyle, measurementStyleWithCenter],
+ updateWhileAnimating: true,
+ updateWhileInteracting: true,
+ zIndex: LayerProperties.MEASUREMENT.zIndex
+ })
+ )
+
+ const dispatch = useMainAppDispatch()
+ const { circleMeasurementToAdd, measurementsDrawed, measurementTypeToAdd } = useMainAppSelector(
+ state => state.measurement
+ )
+
+ const [measurementInProgress, setMeasurementInProgress] = useState(null)
+ const measurementInProgressRef = useRef(measurementInProgress)
+ const setMeasurementInProgressWithRef = (nextMeasurementInProgress: MeasurementInProgress | null) => {
+ measurementInProgressRef.current = nextMeasurementInProgress
+ setMeasurementInProgress(nextMeasurementInProgress)
+ }
+ const [drawObject, setDrawObject] = useState(null)
+
+ const addCustomCircleMeasurement = useCallback(
+ (nextCircleMeasurementToAdd: { circleCoordinatesToAdd: Coordinates; circleRadiusToAdd: number }) => {
+ const metersForOneNauticalMile = 1852
+ const longitude = 1
+ const latitude = 0
+ const numberOfVertices = 64
+
+ if (
+ !circleMeasurementHasCoordinatesAndRadiusFromForm() &&
+ !circleMeasurementHasRadiusFromFormAndCoordinatesFromDraw()
+ ) {
+ return
+ }
+
+ function circleMeasurementHasCoordinatesAndRadiusFromForm() {
+ return (
+ nextCircleMeasurementToAdd.circleCoordinatesToAdd?.length === 2 &&
+ nextCircleMeasurementToAdd.circleRadiusToAdd
+ )
+ }
+
+ function circleMeasurementHasRadiusFromFormAndCoordinatesFromDraw() {
+ return nextCircleMeasurementToAdd.circleRadiusToAdd && measurementInProgress?.center?.length === 2
+ }
+
+ assertNotNullish(nextCircleMeasurementToAdd.circleRadiusToAdd)
+
+ const radiusInMeters = METERS_PER_UNIT.m * nextCircleMeasurementToAdd.circleRadiusToAdd * metersForOneNauticalMile
+ let coordinates: Coordinate = []
+ if (circleMeasurementHasCoordinatesAndRadiusFromForm()) {
+ coordinates = [
+ nextCircleMeasurementToAdd.circleCoordinatesToAdd[longitude],
+ nextCircleMeasurementToAdd.circleCoordinatesToAdd[latitude]
+ ]
+ } else if (circleMeasurementHasRadiusFromFormAndCoordinatesFromDraw()) {
+ assertNotNullish(measurementInProgress?.center)
+
+ coordinates = transform(measurementInProgress.center, OPENLAYERS_PROJECTION, WSG84_PROJECTION)
+ }
+
+ const circleFeature = new Feature({
+ geometry: circular(coordinates, radiusInMeters, numberOfVertices).transform(
+ WSG84_PROJECTION,
+ OPENLAYERS_PROJECTION
+ ),
+ style: [measurementStyle, measurementStyleWithCenter]
+ })
+ dispatch(saveMeasurement(circleFeature, `r = ${nextCircleMeasurementToAdd.circleRadiusToAdd} nm`))
+ },
+ [dispatch, measurementInProgress?.center]
+ )
+
+ const deleteFeature = useCallback(
+ (featureId: string) => {
+ const feature = vectorSourceRef.current.getFeatureById(featureId)
+ if (feature) {
+ vectorSourceRef.current.removeFeature(feature)
+ vectorSourceRef.current.changed()
+ }
+
+ dispatch(removeMeasurementDrawed(featureId))
+ },
+ [dispatch]
+ )
+
+ const startDrawing = useCallback((event: DrawEvent) => {
+ // TODO Fix TS error `Property 'coordinate' does not exist on type 'DrawEvent'`.
+ // @ts-ignore
+ let firstTooltipCoordinates = event.coordinate
+ const geometry = event.feature.getGeometry()
+ assertNotNullish(geometry)
+
+ setMeasurementInProgressWithRef({
+ center: getCenter(geometry.getExtent()),
+ // TODO Fix TS error `Property 'getLastCoordinate' does not exist on type 'Geometry'`.
+ // @ts-ignore
+ coordinates: geometry.getLastCoordinate(),
+ measurement: 0
+ })
+
+ return geometry.on('change', changeEvent => {
+ const geom = changeEvent.target
+
+ if (geom instanceof LineString) {
+ const nextMeasurementOutput = getNauticalMilesOfLine(geom)
+ firstTooltipCoordinates = geom.getLastCoordinate()
+
+ setMeasurementInProgressWithRef({
+ coordinates: firstTooltipCoordinates,
+ measurement: nextMeasurementOutput
+ })
+ } else if (geom instanceof Circle) {
+ const nextMeasurementOutput = getNauticalMilesRadiusOfCircle(geom)
+ firstTooltipCoordinates = geom.getLastCoordinate()
+
+ setMeasurementInProgressWithRef({
+ center: getCenter(geom.getExtent()),
+ coordinates: firstTooltipCoordinates,
+ measurement: nextMeasurementOutput
+ })
+ }
+ })
+ }, [])
+
+ useEffect(() => {
+ const vectorLayer = vectorLayerRef.current
+
+ monitorfishMap.getLayers().push(vectorLayer)
+
+ return () => {
+ monitorfishMap.removeLayer(vectorLayer)
+ }
+ }, [])
+
+ useEffect(() => {
+ if (measurementsDrawed) {
+ measurementsDrawed.forEach(measurement => {
+ const feature = new GeoJSON({
+ featureProjection: OPENLAYERS_PROJECTION
+ }).readFeature(measurement.feature)
+
+ vectorSourceRef.current.addFeature(feature)
+ })
+ }
+ }, [measurementsDrawed])
+
+ useEffect(() => {
+ if (measurementTypeToAdd) {
+ setMeasurementInProgressWithRef({
+ coordinates: null,
+ feature: null,
+ measurement: null
+ })
+
+ const draw = new Draw({
+ source: vectorSourceRef.current,
+ style: [measurementStyle, measurementStyleWithCenter],
+ type: measurementTypeToAdd
+ })
+
+ monitorfishMap.addInteraction(draw)
+ setDrawObject(draw)
+ }
+ }, [measurementTypeToAdd])
+
+ useEffect(() => {
+ if (!measurementTypeToAdd && drawObject) {
+ setDrawObject(null)
+ setMeasurementInProgressWithRef(null)
+
+ setTimeout(() => {
+ monitorfishMap.removeInteraction(drawObject)
+ }, 300)
+ }
+ }, [drawObject, measurementTypeToAdd])
+
+ useEffect(() => {
+ if (circleMeasurementToAdd) {
+ addCustomCircleMeasurement(circleMeasurementToAdd)
+ }
+ }, [addCustomCircleMeasurement, circleMeasurementToAdd])
+
+ useEffect(() => {
+ if (!drawObject) {
+ return
+ }
+
+ let listener
+
+ drawObject.on(DRAW_START_EVENT, event => {
+ listener = startDrawing(event)
+ })
+
+ drawObject.on(DRAW_ABORT_EVENT, () => {
+ unByKey(listener)
+
+ dispatch(resetMeasurementTypeToAdd())
+
+ setMeasurementInProgressWithRef(null)
+ })
+
+ drawObject.on(DRAW_END_EVENT, event => {
+ assertNotNullish(measurementInProgressRef.current)
+
+ unByKey(listener)
+
+ dispatch(saveMeasurement(event.feature, measurementInProgressRef.current.measurement))
+ dispatch(resetMeasurementTypeToAdd())
+
+ setMeasurementInProgressWithRef(null)
+ })
+ }, [dispatch, drawObject, startDrawing])
+
+ useEffect(() => {
+ if (!!measurementInProgress?.center || !!measurementInProgress?.measurement) {
+ dispatch(
+ setCircleMeasurementInDrawing({
+ coordinates: measurementInProgress.center,
+ measurement: measurementInProgress.measurement
+ })
+ )
+ }
+ }, [dispatch, measurementInProgress])
+
+ return (
+ <>
+ {measurementsDrawed.map(measurement => (
+
+ ))}
+
+
+ {measurementInProgress ? (
+
+ ) : null}
+
+ >
+ )
+}
+
+export const MeasurementLayer = memo(UnmemoizedMeasurementLayer)
diff --git a/frontend/src/features/Measurement/layers/measurement.style.jsx b/frontend/src/features/Measurement/layers/measurement.style.tsx
similarity index 61%
rename from frontend/src/features/Measurement/layers/measurement.style.jsx
rename to frontend/src/features/Measurement/layers/measurement.style.tsx
index 127026bb35..5c6642c619 100644
--- a/frontend/src/features/Measurement/layers/measurement.style.jsx
+++ b/frontend/src/features/Measurement/layers/measurement.style.tsx
@@ -1,32 +1,35 @@
-import Style from 'ol/style/Style'
+import { THEME } from '@mtes-mct/monitor-ui'
+import { assertNotNullish } from '@utils/assertNotNullish'
+import { getCenter } from 'ol/extent'
+import Point from 'ol/geom/Point'
+import CircleStyle from 'ol/style/Circle'
import Fill from 'ol/style/Fill'
import Stroke from 'ol/style/Stroke'
-import { COLORS } from '@constants/constants'
-import CircleStyle from 'ol/style/Circle'
-import Point from 'ol/geom/Point'
-import { getCenter } from 'ol/extent'
+import Style from 'ol/style/Style'
export const measurementStyleWithCenter = new Style({
- image: new CircleStyle({
- radius: 2,
- fill: new Fill({
- color: COLORS.slateGray
- })
- }),
geometry: feature => {
- if (feature.getGeometry().getType() === 'LineString') {
+ if (feature.getGeometry()?.getType() === 'LineString') {
return undefined
}
- const extent = feature.getGeometry().getExtent()
+ const extent = feature.getGeometry()?.getExtent()
+ assertNotNullish(extent)
const center = getCenter(extent)
+
return new Point(center)
- }
+ },
+ image: new CircleStyle({
+ fill: new Fill({
+ color: THEME.color.slateGray
+ }),
+ radius: 2
+ })
})
export const measurementStyle = new Style({
stroke: new Stroke({
- color: COLORS.slateGray,
+ color: THEME.color.slateGray,
lineDash: [4, 4],
width: 2
})
diff --git a/frontend/src/features/Measurement/slice.ts b/frontend/src/features/Measurement/slice.ts
index d1b8fbcbe6..031194d586 100644
--- a/frontend/src/features/Measurement/slice.ts
+++ b/frontend/src/features/Measurement/slice.ts
@@ -3,6 +3,7 @@ import { createSlice } from '@reduxjs/toolkit'
import { getLocalStorageState } from '../../utils'
import type { MeasurementType } from '../../domain/entities/map/constants'
+import type { Coordinates } from '@mtes-mct/monitor-ui'
const measurementsLocalStorageKey = 'measurements'
@@ -12,8 +13,10 @@ export type MeasurementState = {
coordinates: number[]
measurement: any
} | null
- // TODO Type this prop.
- circleMeasurementToAdd: null
+ circleMeasurementToAdd: {
+ circleCoordinatesToAdd: Coordinates
+ circleRadiusToAdd: number
+ } | null
measurementTypeToAdd: MeasurementType | null
// TODO Type this prop.
measurementsDrawed: Record[]
diff --git a/frontend/src/features/PriorNotification/components/PriorNotificationCard/index.tsx b/frontend/src/features/PriorNotification/components/PriorNotificationCard/index.tsx
index 31fefa3504..0a6a7755c7 100644
--- a/frontend/src/features/PriorNotification/components/PriorNotificationCard/index.tsx
+++ b/frontend/src/features/PriorNotification/components/PriorNotificationCard/index.tsx
@@ -101,7 +101,7 @@ export function PriorNotificationCard({
/>
{isPendingVerification && Le préavis doit être vérifié par le CNSP avant sa diffusion.}
-
+
Le navire doit respecter un délai d’envoi{hasDesignatedPorts && ' et débarquer dans un port désigné'}.
@@ -155,9 +155,9 @@ const Body = styled.div`
`
const Intro = styled.p<{
- hasNoTopMargin?: boolean
+ $hasNoTopMargin?: boolean
}>`
- ${p => p.hasNoTopMargin && 'margin-top: 2px;'}
+ ${p => p.$hasNoTopMargin && 'margin-top: 2px;'}
color: ${p => p.theme.color.slateGray};
font-style: italic;
`
diff --git a/frontend/src/features/Reporting/components/ReportingList/index.tsx b/frontend/src/features/Reporting/components/ReportingList/index.tsx
index 6da6123705..e79f4c3bff 100644
--- a/frontend/src/features/Reporting/components/ReportingList/index.tsx
+++ b/frontend/src/features/Reporting/components/ReportingList/index.tsx
@@ -154,7 +154,7 @@ MMSI: ${reporting.mmsi || ''}`
+
)
@@ -34,16 +33,16 @@ export function EditReporting() {
return (
-
+
ÉDITER LE SIGNALEMENT
dispatch(setEditedReportingInSideWindow())} />
-
+
{editedReportingInSideWindow && editedReportingInSideWindow.flagState && (
)}
@@ -73,11 +72,11 @@ export function EditReporting() {
}
const EditReportingWrapper = styled.div<{
- isEditedInSideWindow: boolean
+ $isEditedInSideWindow: boolean
}>`
- background: ${THEME.color.white};
+ background: ${p => p.theme.color.white};
height: 100vh;
- margin-right: ${p => (p.isEditedInSideWindow ? 0 : -490)}px;
+ margin-right: ${p => (p.$isEditedInSideWindow ? 0 : -490)}px;
position: fixed;
right: 0px;
top: 0px;
@@ -91,13 +90,11 @@ const StyledReportingForm = styled(ReportingForm)`
`
const Line = styled.div`
- border-bottom: 1px solid ${THEME.color.lightGray};
+ border-bottom: 1px solid ${p => p.theme.color.lightGray};
width: 100%;
`
-const Flag = styled.img<{
- rel?: 'preload'
-}>`
+const Flag = styled.img`
cursor: pointer;
display: inline-block;
height: 14;
@@ -106,7 +103,7 @@ const Flag = styled.img<{
`
const VesselName = styled.div`
- color: ${THEME.color.gunMetal};
+ color: ${p => p.theme.color.gunMetal};
font: normal normal bold 16px/22px Marianne;
margin-left: 8px;
overflow: hidden;
@@ -115,7 +112,7 @@ const VesselName = styled.div`
`
const InternalReferenceNumber = styled.div`
- color: ${THEME.color.gunMetal};
+ color: ${p => p.theme.color.gunMetal};
font: normal normal normal 16px/22px Marianne;
margin-left: 5;
`
@@ -130,10 +127,10 @@ const CloseIcon = styled(CloseIconSVG)`
`
const Row = styled.div<{
- topMargin: number
+ $topMargin: number
}>`
display: flex;
- margin-top: ${p => p.topMargin};
+ margin-top: ${p => p.$topMargin};
`
const Header = styled.div`
@@ -141,7 +138,7 @@ const Header = styled.div`
`
const Title = styled.span`
- color: ${THEME.color.slateGray};
+ color: ${p => p.theme.color.slateGray};
font: normal normal bold 16px Marianne;
letter-spacing: 0px;
margin-left: 10px;
diff --git a/frontend/src/features/SideWindow/Alert/AlertListAndReportingList/index.tsx b/frontend/src/features/SideWindow/Alert/AlertListAndReportingList/index.tsx
index b9d3f51286..155f1f4d81 100644
--- a/frontend/src/features/SideWindow/Alert/AlertListAndReportingList/index.tsx
+++ b/frontend/src/features/SideWindow/Alert/AlertListAndReportingList/index.tsx
@@ -39,14 +39,14 @@ export function AlertListAndReportingList({
return (
setSelectedTab(AlertAndReportingTab.ALERT)}
>
Alertes
setSelectedTab(AlertAndReportingTab.REPORTING)}
>
Signalements
@@ -75,9 +75,9 @@ const Wrapper = styled.div`
// TODO This should be a `` or a ``.
const Title = styled.h2<{
- isSelected: boolean
+ $isSelected: boolean
}>`
- border-bottom: 5px solid ${p => (p.isSelected ? p.theme.color.charcoal : p.theme.color.white)};
+ border-bottom: 5px solid ${p => (p.$isSelected ? p.theme.color.charcoal : p.theme.color.white)};
color: ${p => p.theme.color.gunMetal};
cursor: pointer;
display: inline-block;
diff --git a/frontend/src/features/map/Map.tsx b/frontend/src/features/map/Map.tsx
index 29450aeab8..01c21aa089 100644
--- a/frontend/src/features/map/Map.tsx
+++ b/frontend/src/features/map/Map.tsx
@@ -29,8 +29,8 @@ import { FeatureWithCodeAndEntityId } from '../../libs/FeatureWithCodeAndEntityI
import { AdministrativeLayers } from '../AdministrativeZone/layers/AdministrativeLayers'
import { BaseLayer } from '../BaseMap/layers/BaseLayer'
import { DrawLayer } from '../Draw/layer'
-import InterestPointLayer from '../InterestPoint/layers/InterestPointLayer'
-import MeasurementLayer from '../Measurement/layers/MeasurementLayer'
+import { InterestPointLayer } from '../InterestPoint/layers/InterestPointLayer'
+import { MeasurementLayer } from '../Measurement/layers/MeasurementLayer'
import { MissionOverlay } from '../Mission/components/MissionOverlay'
import { SelectedMissionOverlay } from '../Mission/components/SelectedMissionOverlay'
import { MissionHoveredLayer } from '../Mission/layers/HoveredMissionLayer'
@@ -45,6 +45,8 @@ import { HoveredStationOverlay } from '../Station/components/HoveredStationOverl
import { SelectedStationOverlay } from '../Station/components/SelectedStationOverlay'
import { StationLayer } from '../Station/components/StationLayer'
+import type { DummyObjectToForceEffectHookUpdate } from './types'
+
export function Map() {
const isSuperUser = useIsSuperUser()
const { areVesselsDisplayed, isMissionsLayerDisplayed, isStationLayerDisplayed } = useMainAppSelector(
@@ -53,7 +55,7 @@ export function Map() {
const [shouldUpdateView, setShouldUpdateView] = useState(true)
const [historyMoveTrigger, setHistoryMoveTrigger] = useState({})
const [hoveredFeature, setHoveredFeature] = useState(undefined)
- const [mapMovingAndZoomEvent, setMapMovingAndZoomEvent] = useState