|
| 1 | +import React, { useEffect, useRef, useState } from 'react'; |
| 2 | +import PropTypes from 'prop-types'; |
| 3 | +import maplibregl from 'maplibre-gl'; |
| 4 | +import ReactMapGL, { AttributionControl, ScaleControl } from 'react-map-gl'; |
| 5 | +import { point as turfPoint } from '@turf/helpers'; |
| 6 | +import { useSelector } from 'react-redux'; |
| 7 | +import turfNearestPointOnLine from '@turf/nearest-point-on-line'; |
| 8 | + |
| 9 | +/* Main data & layers */ |
| 10 | +import Background from 'common/Map/Layers/Background'; |
| 11 | +import VirtualLayers from 'applications/osrd/views/OSRDSimulation/VirtualLayers'; |
| 12 | +import SnappedMarker from 'common/Map/Layers/SnappedMarker'; |
| 13 | +/* Objects & various */ |
| 14 | +import TracksGeographic from 'common/Map/Layers/TracksGeographic'; |
| 15 | +import colors from 'common/Map/Consts/colors'; |
| 16 | +import osmBlankStyle from 'common/Map/Layers/osmBlankStyle'; |
| 17 | +import { LAYER_GROUPS_ORDER, LAYERS } from 'config/layerOrder'; |
| 18 | + |
| 19 | +import 'common/Map/Map.scss'; |
| 20 | +import OperationalPoints from 'common/Map/Layers/OperationalPoints'; |
| 21 | +import Platforms from 'common/Map/Layers/Platforms'; |
| 22 | +import { getMapMouseEventNearestFeature } from 'utils/mapboxHelper'; |
| 23 | + |
| 24 | +export default function Map(props) { |
| 25 | + const { viewport, setViewport, setClickedFeature } = props; |
| 26 | + const mapStyle = useSelector((state) => state.map.mapStyle); |
| 27 | + const [lngLatHover, setLngLatHover] = useState(); |
| 28 | + const [trackSectionGeoJSON, setTrackSectionGeoJSON] = useState(); |
| 29 | + const [snappedPoint, setSnappedPoint] = useState(); |
| 30 | + const mapRef = useRef(null); |
| 31 | + const scaleControlStyle = { |
| 32 | + left: 20, |
| 33 | + bottom: 20, |
| 34 | + }; |
| 35 | + |
| 36 | + const onFeatureClick = (e) => { |
| 37 | + const result = getMapMouseEventNearestFeature(e); |
| 38 | + if ( |
| 39 | + result && |
| 40 | + result.feature.properties && |
| 41 | + result.feature.properties.id && |
| 42 | + result.feature.geometry.type === 'LineString' |
| 43 | + ) { |
| 44 | + setClickedFeature(result.feature); |
| 45 | + } |
| 46 | + }; |
| 47 | + |
| 48 | + const onMoveGetFeature = (e) => { |
| 49 | + const result = getMapMouseEventNearestFeature(e); |
| 50 | + if ( |
| 51 | + result && |
| 52 | + result.feature.properties && |
| 53 | + result.feature.properties.id && |
| 54 | + result.feature.geometry.type === 'LineString' |
| 55 | + ) { |
| 56 | + setTrackSectionGeoJSON(result.feature.geometry); |
| 57 | + setLngLatHover(result.nearest); |
| 58 | + } else { |
| 59 | + setSnappedPoint(undefined); |
| 60 | + } |
| 61 | + }; |
| 62 | + |
| 63 | + useEffect(() => { |
| 64 | + if (trackSectionGeoJSON !== undefined && lngLatHover !== undefined) { |
| 65 | + const point = turfPoint(lngLatHover); |
| 66 | + try { |
| 67 | + setSnappedPoint(turfNearestPointOnLine(trackSectionGeoJSON, point)); |
| 68 | + } catch (error) { |
| 69 | + console.warn(`Ìmpossible to snapPoint - error ${error}`); |
| 70 | + } |
| 71 | + } |
| 72 | + }, [trackSectionGeoJSON, lngLatHover]); |
| 73 | + |
| 74 | + return ( |
| 75 | + <ReactMapGL |
| 76 | + ref={mapRef} |
| 77 | + {...viewport} |
| 78 | + style={{ cursor: 'pointer' }} |
| 79 | + width="100%" |
| 80 | + height="100%" |
| 81 | + mapLib={maplibregl} |
| 82 | + mapStyle={osmBlankStyle} |
| 83 | + onMove={(e) => setViewport(e.viewState)} |
| 84 | + onMouseMove={(e) => onMoveGetFeature(e)} |
| 85 | + attributionControl={false} // Defined below |
| 86 | + onClick={onFeatureClick} |
| 87 | + interactiveLayerIds={['chartis/tracks-geo/main']} |
| 88 | + touchZoomRotate |
| 89 | + > |
| 90 | + <VirtualLayers /> |
| 91 | + <AttributionControl className="attribution-control" customAttribution="©SNCF Réseau" /> |
| 92 | + <ScaleControl maxWidth={100} unit="metric" style={scaleControlStyle} /> |
| 93 | + |
| 94 | + <Background |
| 95 | + colors={colors[mapStyle]} |
| 96 | + layerOrder={LAYER_GROUPS_ORDER[LAYERS.BACKGROUND.GROUP]} |
| 97 | + /> |
| 98 | + |
| 99 | + <Platforms |
| 100 | + colors={colors[mapStyle]} |
| 101 | + layerOrder={LAYER_GROUPS_ORDER[LAYERS.PLATFORMS.GROUP]} |
| 102 | + /> |
| 103 | + |
| 104 | + <TracksGeographic |
| 105 | + colors={colors[mapStyle]} |
| 106 | + layerOrder={LAYER_GROUPS_ORDER[LAYERS.TRACKS_GEOGRAPHIC.GROUP]} |
| 107 | + /> |
| 108 | + <OperationalPoints |
| 109 | + geomType="geo" |
| 110 | + colors={colors[mapStyle]} |
| 111 | + layerOrder={LAYER_GROUPS_ORDER[LAYERS.OPERATIONAL_POINTS.GROUP]} |
| 112 | + /> |
| 113 | + {snappedPoint !== undefined ? <SnappedMarker geojson={snappedPoint} /> : null} |
| 114 | + </ReactMapGL> |
| 115 | + ); |
| 116 | +} |
| 117 | + |
| 118 | +Map.propTypes = { |
| 119 | + viewport: PropTypes.object.isRequired, |
| 120 | + setViewport: PropTypes.func.isRequired, |
| 121 | + setClickedFeature: PropTypes.func.isRequired, |
| 122 | +}; |
0 commit comments