Skip to content

Commit

Permalink
front: select op with map when add train
Browse files Browse the repository at this point in the history
-bold pr on hover (for PRs on same track)
-select pr on map and choose track

Signed-off-by: theocrsb <[email protected]>
  • Loading branch information
theocrsb committed Jan 3, 2025
1 parent f02812f commit b1bbf6e
Show file tree
Hide file tree
Showing 9 changed files with 229 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"addVia": "Add this waypoint",
"addVias": "Add waypoints",
"addTrainSchedule": "Add one or several trains",
"anyTrack": "Any track",
"blocktype": "Signalling block type",
"BoundsAreLinked": "Both bounds are linked",
"BoundsAreNotLinked": "Both bounds are not linked",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"addVia": "Ajouter ce point de passage",
"addVias": "Ajout de points de passage",
"addTrainSchedule": "Ajouter un ou plusieurs trains",
"anyTrack": "Toutes voies",
"blocktype": "Type de block",
"BoundsAreLinked": "Les deux bornes sont liées",
"BoundsAreNotLinked": "Les deux bornes ne sont pas liées",
Expand Down
22 changes: 19 additions & 3 deletions front/src/common/Map/Layers/OperationalPoints.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,15 @@ interface Props {
colors: Theme;
layerOrder: number;
infraID: number | undefined;
operationnalPointId?: string;
}

export default function OperationalPoints({ colors, layerOrder, infraID }: Props) {
export default function OperationalPoints({
colors,
layerOrder,
infraID,
operationnalPointId,
}: Props) {
const point: LayerProps = {
type: 'circle',
'source-layer': 'operational_points',
Expand Down Expand Up @@ -42,7 +48,12 @@ export default function OperationalPoints({ colors, layerOrder, infraID }: Props
['concat', ' ', ['get', 'extensions_sncf_ch']],
],
],
'text-font': ['Roboto Condensed'],
'text-font': [
'case',
['==', ['get', 'id'], operationnalPointId || ''],
['literal', ['Roboto Bold']],
['literal', ['Roboto Condensed']],
],
'text-size': 12,
'text-anchor': 'left',
'text-justify': 'left',
Expand Down Expand Up @@ -100,7 +111,12 @@ export default function OperationalPoints({ colors, layerOrder, infraID }: Props
['get', 'extensions_sncf_ch'],
],
],
'text-font': ['Roboto Condensed'],
'text-font': [
'case',
['==', ['get', 'id'], operationnalPointId || ''],
['literal', ['Roboto Bold']],
['literal', ['Roboto Condensed']],
],
'text-size': 11,
'text-anchor': 'left',
'text-allow-overlap': false,
Expand Down
24 changes: 20 additions & 4 deletions front/src/modules/pathfinding/helpers/getStepLocation.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,35 @@
import type { PathItemLocation } from 'common/api/osrdEditoastApi';
import type { OperationalPointReference, PathItemLocation } from 'common/api/osrdEditoastApi';
import { mToMm } from 'utils/physics';

const getStepLocation = (step: PathItemLocation): PathItemLocation => {
const trackReference: OperationalPointReference['track_reference'] =
!('track' in step) && step.track_reference && 'track_name' in step.track_reference
? { track_name: step.track_reference.track_name }
: null;

if ('track' in step) {
// TODO: step offset should be in mm in the store /!\
// pathfinding blocks endpoint requires offsets in mm
return { track: step.track, offset: mToMm(step.offset) };
}
if ('operational_point' in step) {
return { operational_point: step.operational_point };
return { operational_point: step.operational_point, track_reference: trackReference };
}
if ('trigram' in step) {
return { trigram: step.trigram, secondary_code: step.secondary_code };
return {
trigram: step.trigram,
secondary_code: step.secondary_code,
track_reference: trackReference,
};
}
if (step.uic === -1) {
throw new Error('Invalid UIC');
}
return { uic: step.uic, secondary_code: step.secondary_code };
return {
uic: step.uic,
secondary_code: step.secondary_code,
track_reference: trackReference,
};
};

export default getStepLocation;
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import React, { useEffect, useState } from 'react';
import React, { useEffect, useState, useMemo } from 'react';

import { Select } from '@osrd-project/ui-core';
import { point } from '@turf/helpers';
import { omit } from 'lodash';
import { useTranslation } from 'react-i18next';
import { IoFlag } from 'react-icons/io5';
import { RiMapPin2Fill, RiMapPin3Fill } from 'react-icons/ri';
Expand All @@ -13,11 +15,13 @@ import { editoastToEditorEntity } from 'applications/editor/data/api';
import type { TrackSectionEntity } from 'applications/editor/tools/trackEdition/types';
import { calculateDistanceAlongTrack } from 'applications/editor/tools/utils';
import { useManageTrainScheduleContext } from 'applications/operationalStudies/hooks/useManageTrainScheduleContext';
import { useScenarioContext } from 'applications/operationalStudies/hooks/useScenarioContext';
import type { ManageTrainSchedulePathProperties } from 'applications/operationalStudies/types';
import { osrdEditoastApi } from 'common/api/osrdEditoastApi';
import { osrdEditoastApi, type OperationalPoint } from 'common/api/osrdEditoastApi';
import { useOsrdConfSelectors } from 'common/osrdContext';
import { setPointIti } from 'modules/trainschedule/components/ManageTrainSchedule/ManageTrainScheduleMap/setPointIti';
import type { PathStep } from 'reducers/osrdconf/types';
import { type PathStep } from 'reducers/osrdconf/types';
import { getPointCoordinates } from 'utils/geometry';

import type { FeatureInfoClick } from '../types';

Expand All @@ -27,30 +31,50 @@ type AddPathStepPopupProps = {
resetFeatureInfoClick: () => void;
};

function AddPathStepPopup({
const AddPathStepPopup = ({
pathProperties,
featureInfoClick,
resetFeatureInfoClick,
}: AddPathStepPopupProps) {
}: AddPathStepPopupProps) => {
const { getInfraID, getOrigin, getDestination } = useOsrdConfSelectors();
const { launchPathfinding } = useManageTrainScheduleContext();
const { t } = useTranslation(['operationalStudies/manageTrainSchedule']);
const infraId = useSelector(getInfraID);
const origin = useSelector(getOrigin);
const destination = useSelector(getDestination);

const isOperationalPoint = useMemo(() => {
const properties = featureInfoClick?.feature?.properties;
return !!properties?.track_id || !!properties?.track_name;
}, [featureInfoClick]);

const { getTrackSectionsByIds } = useScenarioContext();

const [trackOffset, setTrackOffset] = useState(0);
const [clickedOp, setClickedOp] = useState<
Extract<PathStep, { uic: number }> & {
tracks: {
trackName?: string;
coordinates?: number[];
}[];
}
>();
const [selectedTrack, setSelectedTrack] = useState<{
trackName?: string;
coordinates?: number[];
}>();

const [getTrackEntity] =
const [getInfraObjectEntity] =
osrdEditoastApi.endpoints.postInfraByInfraIdObjectsAndObjectType.useLazyQuery();

useEffect(() => {
const calculateOffset = async () => {
const trackId = featureInfoClick.feature.properties?.id;
const result = await getTrackEntity({
const handleTrack = async () => {
const objectId = featureInfoClick.feature.properties?.id;

const result = await getInfraObjectEntity({
infraId: infraId!,
objectType: 'TrackSection',
body: [trackId],
body: [objectId],
}).unwrap();

if (!result.length) {
Expand All @@ -67,49 +91,127 @@ function AddPathStepPopup({
setTrackOffset(offset);
};

calculateOffset();
const handleOperationalPoint = async () => {
const objectId = featureInfoClick.feature.properties?.id;

const result = await getInfraObjectEntity({
infraId: infraId!,
objectType: 'OperationalPoint',
body: [objectId],
}).unwrap();

if (!result.length) {
console.error('No operational point found');
return;
}

const operationalPoint = result[0].railjson as OperationalPoint;
const trackIds = operationalPoint.parts.map((part) => part.track);
const tracks = await getTrackSectionsByIds(trackIds);

const trackPartCoordinates = operationalPoint.parts.map((part) => ({
trackName: tracks[part.track]?.extensions?.sncf?.track_name,
coordinates: getPointCoordinates(
tracks[part.track]?.geo,
tracks[part.track]?.length,
part.position
),
}));

trackPartCoordinates.unshift({
trackName: undefined,
coordinates: result[0].geographic.coordinates as number[],
});

setClickedOp({
id: nextId(),
secondary_code: operationalPoint.extensions!.sncf!.ch,
uic: operationalPoint.extensions!.identifier!.uic,
tracks: trackPartCoordinates,
});
setSelectedTrack(trackPartCoordinates[0]);
};

setClickedOp(undefined);

if (isOperationalPoint) {
handleOperationalPoint();
} else {
handleTrack();
}
}, [featureInfoClick]);

if (!featureInfoClick.feature.properties) return null;

const { properties: trackProperties } = featureInfoClick.feature;
const coordinates = featureInfoClick.coordinates.slice(0, 2);

const pathStepProperties: PathStep = {
id: nextId(),
coordinates,
track: trackProperties.id,
offset: Math.round(trackOffset), // offset needs to be an integer
kp: trackProperties.kp,
metadata: {
lineCode: trackProperties.extensions_sncf_line_code,
lineName: trackProperties.extensions_sncf_line_name,
trackName: trackProperties.extensions_sncf_track_name,
trackNumber: trackProperties.extensions_sncf_track_number,
},
};
let pathStepProperties: PathStep;
if (isOperationalPoint && clickedOp && selectedTrack) {
const newPathStep: PathStep = {
...omit(clickedOp, ['tracks']),
coordinates: selectedTrack.coordinates,
track_reference: selectedTrack.trackName
? { track_name: selectedTrack.trackName }
: undefined,
};
pathStepProperties = {
...newPathStep,
};
} else {
pathStepProperties = {
id: nextId(),
coordinates,
track: trackProperties.id,
offset: Math.round(trackOffset), // offset needs to be an integer
kp: trackProperties.kp,
metadata: {
lineCode: trackProperties.extensions_sncf_line_code,
lineName: trackProperties.extensions_sncf_line_name,
trackName: trackProperties.extensions_sncf_track_name,
trackNumber: trackProperties.extensions_sncf_track_number,
},
};
}

return (
<Popup
longitude={featureInfoClick.coordinates[0]}
latitude={featureInfoClick.coordinates[1]}
longitude={coordinates[0]}
latitude={coordinates[1]}
closeButton={false}
closeOnClick={false}
className="map-popup-click-select"
>
<div className="details">
<div className="details-track">
{featureInfoClick.feature.properties.extensions_sncf_track_name}
<small>{featureInfoClick.feature.properties.extensions_sncf_line_code}</small>
{isOperationalPoint && trackProperties.extensions_sncf_track_name}
<small>{trackProperties.extensions_sncf_line_code}</small>
</div>
<div className="details-line">
{featureInfoClick.feature.properties.extensions_sncf_line_name}
{isOperationalPoint ? (
<>
{trackProperties.extensions_identifier_name} <br />
{trackProperties.extensions_sncf_trigram} {trackProperties.extensions_sncf_ch}
</>
) : (
trackProperties.extensions_sncf_line_name
)}
</div>
</div>

{isOperationalPoint && clickedOp?.tracks && (
<Select
getOptionLabel={(option) => option?.trackName || t('anyTrack')}
getOptionValue={(option) => option?.trackName || ''}
id="select-track"
onChange={(selectedOption) => setSelectedTrack(selectedOption)}
options={clickedOp.tracks}
value={selectedTrack}
/>
)}

<div className="actions">
<button
data-testid="map-origin-button"
className="btn btn-sm btn-success"
type="button"
onClick={() =>
Expand All @@ -123,22 +225,21 @@ function AddPathStepPopup({
<button
className="btn btn-sm btn-info"
type="button"
onClick={() =>
onClick={() => {
setPointIti(
'via',
pathStepProperties,
launchPathfinding,
resetFeatureInfoClick,
pathProperties
)
}
);
}}
>
<RiMapPin3Fill />
<span className="d-none">{t('via')}</span>
</button>
)}
<button
data-testid="map-destination-button"
className="btn btn-sm btn-warning"
type="button"
onClick={() =>
Expand All @@ -151,6 +252,6 @@ function AddPathStepPopup({
</div>
</Popup>
);
}
};

export default React.memo(AddPathStepPopup);
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { addElementAtIndex, replaceElementAtIndex } from 'utils/array';

export function setPointIti(
pointType: 'origin' | 'destination' | 'via',
pathStep: PathStep,
pathStep: Extract<PathStep, { track: string } | { uic: number }>,
launchPathfinding: (newPathSteps: (PathStep | null)[]) => void,
resetFeatureInfoClick: () => void,
pathProperties?: ManageTrainSchedulePathProperties
Expand Down
Loading

0 comments on commit b1bbf6e

Please sign in to comment.