Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

front: select op with map when add train #9790

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can remove the eslint-disable no-explicit-any :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

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]);
Comment on lines +46 to +49
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: can we store in featureInfoClick if it is an operationalPoint or a track ?

This way, you won't have to compute isOperationalPoint and you will just have to do featureInfoClick.isOperationalPoint directly


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,
},
};
}
Comment on lines +149 to +175
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: can you add a state variable like this:

const [newPathStep, setNewPathStep] =
    useState<Extract<PathStep, { track: string } | { uic: number }>>();

This newPathStep will replace your pathStepProperties.

This newPathStep should be set at 2 moments:

  • if you're handling a track, then you can set the pathStep at the end of handleTrack (instead of setting the offset, directly create the pathStep)
  • if you're handling an OP, then you should add a useEffect which will be fired each time the clickedOp or the selectedTrack changes. In this useEffect, you should only update the newPathStep

Can you try that ?

Some remarks:

  • you should return null if newPathStep is not defined, maybe l.147
  • the state var trackOffset should not be useful anymore, I think you can remove it


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
Loading