Skip to content

Commit

Permalink
front: move map markers computation in SimulationResultsMap
Browse files Browse the repository at this point in the history
Signed-off-by: Clara Ni <[email protected]>
  • Loading branch information
clarani committed Feb 12, 2025
1 parent 0a2b1ab commit 966d8b8
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 99 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,12 @@ import { useEffect, useState, useMemo } from 'react';

import { ChevronLeft, ChevronRight } from '@osrd-project/ui-icons';
import cx from 'classnames';
import type { Position } from 'geojson';
import { useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux';

import { type Conflict, type PathfindingResultSuccess } from 'common/api/osrdEditoastApi';
import { type Conflict } from 'common/api/osrdEditoastApi';
import SimulationWarpedMap from 'common/Map/WarpedMap/SimulationWarpedMap';
import ResizableSection from 'common/ResizableSection';
import getPointOnPathCoordinates from 'modules/pathfinding/helpers/getPointOnPathCoordinates';
import getTrackLengthCumulativeSums from 'modules/pathfinding/helpers/getTrackLengthCumulativeSums';
import ManchetteWithSpaceTimeChartWrapper, {
MANCHETTE_WITH_SPACE_TIME_CHART_DEFAULT_HEIGHT,
} from 'modules/simulationResult/components/ManchetteWithSpaceTimeChart/ManchetteWithSpaceTimeChart';
Expand All @@ -29,7 +26,6 @@ import { updateSelectedTrainId } from 'reducers/simulationResults';
import { getTrainIdUsedForProjection } from 'reducers/simulationResults/selectors';
import { useAppDispatch } from 'store';

import { useScenarioContext } from '../hooks/useScenarioContext';
import useSimulationResults from '../hooks/useSimulationResults';
import type { TrainSpaceTimeData } from '../types';

Expand Down Expand Up @@ -59,8 +55,6 @@ const SimulationResults = ({
const { t } = useTranslation('simulation');
const dispatch = useAppDispatch();

const { getTrackSectionsByIds } = useScenarioContext();

const {
selectedTrainSchedule,
selectedTrainRollingStock,
Expand All @@ -73,7 +67,6 @@ const SimulationResults = ({
const trainIdUsedForProjection = useSelector(getTrainIdUsedForProjection);

const [showWarpedMap, setShowWarpedMap] = useState(false);
const [pathItemsCoordinates, setPathItemsCoordinates] = useState<Position[]>();

const [manchetteWithSpaceTimeChartHeight, setManchetteWithSpaceTimeChartHeight] = useState(
MANCHETTE_WITH_SPACE_TIME_CHART_DEFAULT_HEIGHT
Expand All @@ -97,32 +90,6 @@ const SimulationResults = ({
}
}, [projectionData]);

// Compute path items coordinates in order to place them on the map
useEffect(() => {
const getPathItemsCoordinates = async (pathfindingResult: PathfindingResultSuccess) => {
const trackIds = pathfindingResult.track_section_ranges.map((range) => range.track_section);
const tracks = await getTrackSectionsByIds(trackIds);
const tracksLengthCumulativeSums = getTrackLengthCumulativeSums(
pathfindingResult.track_section_ranges
);

const waypointsCoordinates = pathfindingResult.path_item_positions.map((position) =>
getPointOnPathCoordinates(
tracks,
pathfindingResult.track_section_ranges,
tracksLengthCumulativeSums,
position
)
);

setPathItemsCoordinates(waypointsCoordinates);
};

if (path) {
getPathItemsCoordinates(path);
}
}, [path]);

const {
operationalPoints: projectedOperationalPoints,
filteredOperationalPoints,
Expand Down Expand Up @@ -276,8 +243,8 @@ const SimulationResults = ({
trainId: selectedTrainSchedule.id,
startTime: selectedTrainSchedule.start_time,
}}
pathItemsCoordinates={pathItemsCoordinates}
setMapCanvas={setMapCanvas}
pathfindingResult={path}
/>
</div>

Expand Down
52 changes: 52 additions & 0 deletions front/src/common/Map/components/MapMarkers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import cx from 'classnames';
import type { Position } from 'geojson';
import { Marker } from 'react-map-gl/maplibre';

import destinationIcon from 'assets/pictures/mapMarkers/destination.svg';
import viaIcon from 'assets/pictures/mapMarkers/intermediate-point.svg';
import originIcon from 'assets/pictures/mapMarkers/start.svg';
import { MARKER_TYPE } from 'modules/trainschedule/components/ManageTrainSchedule/ManageTrainScheduleMap/ItineraryMarkers';

const MARKER_OFFSET: [number, number] = [0, 8];

export type MapMarker = {
coordinates: Position;
pointType: MARKER_TYPE;
};

type MapMarkersProps = {
markers: MapMarker[];
};

const MapMarkers = ({ markers }: MapMarkersProps) =>
markers.map(({ coordinates, pointType }, index) => {
const viaNumber = markers[0].pointType === MARKER_TYPE.VIA ? index + 1 : index;
let imgSrc = viaIcon;
let imgAlt = `via ${viaNumber}`;

if (pointType === MARKER_TYPE.ORIGIN) {
imgSrc = originIcon;
imgAlt = 'origin';
} else if (pointType === MARKER_TYPE.DESTINATION) {
imgSrc = destinationIcon;
imgAlt = 'destination';
}

return (
<Marker
longitude={coordinates[0]}
latitude={coordinates[1]}
anchor="bottom"
offset={MARKER_OFFSET}
>
<img src={imgSrc} alt={imgAlt} />
{pointType === MARKER_TYPE.VIA && (
<span className={cx('map-pathfinding-marker', 'via-number', 'stdcm-via')}>
{viaNumber}
</span>
)}
</Marker>
);
});

export default MapMarkers;
Original file line number Diff line number Diff line change
@@ -1,23 +1,14 @@
import cx from 'classnames';
import type { Feature, LineString, Position } from 'geojson';
import { Marker, Source } from 'react-map-gl/maplibre';
import type { Feature, LineString } from 'geojson';
import { Source } from 'react-map-gl/maplibre';

import destinationIcon from 'assets/pictures/mapMarkers/destination.svg';
import viaIcon from 'assets/pictures/mapMarkers/intermediate-point.svg';
import originIcon from 'assets/pictures/mapMarkers/start.svg';
import OrderedLayer from 'common/Map/Layers/OrderedLayer';

interface RenderItineraryProps {
type ItineraryProps = {
geojsonPath: Feature<LineString>;
pathItemsCoordinates?: Position[];
layerOrder: number;
}
};

export default function RenderItinerary({
geojsonPath,
pathItemsCoordinates,
layerOrder,
}: RenderItineraryProps) {
const Itinerary = ({ geojsonPath, layerOrder }: ItineraryProps) => {
const paintBackgroundLine = {
'line-width': 4,
'line-color': '#EDF9FF',
Expand All @@ -28,48 +19,8 @@ export default function RenderItinerary({
'line-color': '#158DCF',
};

const markerOffset: [number, number] = [0, 8];

if (!pathItemsCoordinates || pathItemsCoordinates.length < 2) {
return null;
}

const [originLongitude, originLatitude] = pathItemsCoordinates.at(0)!;
const [destinationLongitude, destinationLatitude] = pathItemsCoordinates.at(-1)!;
const vias = pathItemsCoordinates.slice(1, -1);

return (
<Source type="geojson" data={geojsonPath}>
<Marker
longitude={originLongitude}
latitude={originLatitude}
anchor="bottom"
offset={markerOffset}
>
<img src={originIcon} alt="origin" />
</Marker>
{vias.map(([longitude, latitude], index) => (
<Marker
key={`via-${index}`}
longitude={longitude}
latitude={latitude}
anchor="bottom"
offset={markerOffset}
>
<img src={viaIcon} alt={`via ${index + 1}`} />
<span className={cx('map-pathfinding-marker', 'via-number', 'stdcm-via')}>
{index + 1}
</span>
</Marker>
))}
<Marker
longitude={destinationLongitude}
latitude={destinationLatitude}
anchor="bottom"
offset={markerOffset}
>
<img src={destinationIcon} alt="destination" />
</Marker>
<OrderedLayer
id="geojsonPathBackgroundLine"
type="line"
Expand All @@ -80,4 +31,6 @@ export default function RenderItinerary({
<OrderedLayer id="geojsonPathLine" type="line" paint={paintLine} layerOrder={layerOrder} />
</Source>
);
}
};

export default Itinerary;
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,30 @@ import bbox from '@turf/bbox';
import { lineString, point } from '@turf/helpers';
import lineLength from '@turf/length';
import lineSlice from '@turf/line-slice';
import type { Position } from 'geojson';
import type { MapLayerMouseEvent } from 'maplibre-gl';
import type { MapRef } from 'react-map-gl/maplibre';
import { useSelector } from 'react-redux';

import captureMap from 'applications/operationalStudies/helpers/captureMap';
import { useScenarioContext } from 'applications/operationalStudies/hooks/useScenarioContext';
import type {
PathPropertiesFormatted,
SimulationResponseSuccess,
} from 'applications/operationalStudies/types';
import type { PathfindingResultSuccess } from 'common/api/osrdEditoastApi';
import BaseMap from 'common/Map/BaseMap';
import MapButtons from 'common/Map/Buttons/MapButtons';
import MapMarkers, { type MapMarker } from 'common/Map/components/MapMarkers';
import TrainOnMap, { type TrainCurrentInfo } from 'common/Map/components/TrainOnMap/TrainOnMap';
import { removeSearchItemMarkersOnMap } from 'common/Map/utils';
import { computeBBoxViewport } from 'common/Map/WarpedMap/core/helpers';
import { useInfraID } from 'common/osrdContext';
import { LAYER_GROUPS_ORDER, LAYERS } from 'config/layerOrder';
import getPointOnPathCoordinates from 'modules/pathfinding/helpers/getPointOnPathCoordinates';
import getTrackLengthCumulativeSums from 'modules/pathfinding/helpers/getTrackLengthCumulativeSums';
import { MARKER_TYPE } from 'modules/trainschedule/components/ManageTrainSchedule/ManageTrainScheduleMap/ItineraryMarkers';
import { updateViewport, type Viewport } from 'reducers/map';
import { getMap, getTerrain3DExaggeration } from 'reducers/map/selectors';
import { getMap } from 'reducers/map/selectors';
import { getIsPlaying } from 'reducers/simulationResults/selectors';
import { useAppDispatch } from 'store';
import { isoDateWithTimezoneToSec } from 'utils/date';
Expand All @@ -31,35 +36,71 @@ import { kmToM, mmToM, msToKmh } from 'utils/physics';
import getSelectedTrainHoverPositions from './getSelectedTrainHoverPositions';
import { interpolateOnPosition } from '../ChartHelpers/ChartHelpers';
import { useChartSynchronizer } from '../ChartSynchronizer';
import RenderItinerary from './RenderItinerary';
import Itinerary from './RenderItinerary';

const MAP_ID = 'simulation-result-map';

type SimulationResultMapProps = {
pathfindingResult?: PathfindingResultSuccess;
geometry?: PathPropertiesFormatted['geometry'];
trainSimulation?: SimulationResponseSuccess & { trainId: number; startTime: string };
pathItemsCoordinates?: Position[];
setMapCanvas?: (mapCanvas: string) => void;
};

const SimulationResultMap = ({
pathfindingResult,
geometry,
trainSimulation,
pathItemsCoordinates,
setMapCanvas,
}: SimulationResultMapProps) => {
const dispatch = useAppDispatch();

const infraID = useInfraID();
const { viewport, mapSearchMarker, mapStyle, showOSM } = useSelector(getMap);
const { getTrackSectionsByIds } = useScenarioContext();
const { viewport, mapSearchMarker, mapStyle, showOSM, terrain3DExaggeration } =
useSelector(getMap);
const isPlaying = useSelector(getIsPlaying);
const terrain3DExaggeration = useSelector(getTerrain3DExaggeration);

const mapRef = React.useRef<MapRef>(null);
const [selectedTrainHoverPosition, setSelectedTrainHoverPosition] = useState<TrainCurrentInfo>();

const geojsonPath = useMemo(() => geometry && lineString(geometry.coordinates), [geometry]);

const [mapMarkers, setMapMarkers] = useState<MapMarker[]>([]);

// Compute path items coordinates in order to place them on the map
useEffect(() => {
const getPathItemsCoordinates = async (path: PathfindingResultSuccess) => {
const trackIds = path.track_section_ranges.map((range) => range.track_section);
const tracks = await getTrackSectionsByIds(trackIds);
const tracksLengthCumulativeSums = getTrackLengthCumulativeSums(path.track_section_ranges);

const markers = path.path_item_positions.map((position, index) => {
let pointType = MARKER_TYPE.VIA;
if (index === 0) {
pointType = MARKER_TYPE.ORIGIN;
} else if (index === path.path_item_positions.length - 1) {
pointType = MARKER_TYPE.DESTINATION;
}
return {
coordinates: getPointOnPathCoordinates(
tracks,
path.track_section_ranges,
tracksLengthCumulativeSums,
position
),
pointType,
};
});

setMapMarkers(markers);
};

if (pathfindingResult) {
getPathItemsCoordinates(pathfindingResult);
}
}, [pathfindingResult]);

const interactiveLayerIds = useMemo(
() => (geojsonPath ? ['geojsonPath', 'main-train-path'] : []),
[geojsonPath]
Expand Down Expand Up @@ -160,13 +201,14 @@ const SimulationResultMap = ({
terrain3DExaggeration={terrain3DExaggeration}
>
{geojsonPath && (
<RenderItinerary
<Itinerary
geojsonPath={geojsonPath}
layerOrder={LAYER_GROUPS_ORDER[LAYERS.ITINERARY.GROUP]}
pathItemsCoordinates={pathItemsCoordinates}
/>
)}

<MapMarkers markers={mapMarkers} />

{geojsonPath && selectedTrainHoverPosition && trainSimulation && (
<TrainOnMap
trainInfo={selectedTrainHoverPosition}
Expand Down

0 comments on commit 966d8b8

Please sign in to comment.