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

Merged
merged 2 commits into from
Jan 13, 2025
Merged
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
17 changes: 14 additions & 3 deletions front/src/modules/pathfinding/helpers/getStepLocation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,23 @@ const getStepLocation = (step: PathItemLocation): PathItemLocation => {
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: step.track_reference };
}
if ('trigram' in step) {
return { trigram: step.trigram, secondary_code: step.secondary_code };
return {
trigram: step.trigram,
secondary_code: step.secondary_code,
track_reference: step.track_reference,
};
}
return { uic: step.uic, secondary_code: step.secondary_code };
if (step.uic === -1) {
throw new Error('Invalid UIC');
}
return {
uic: step.uic,
secondary_code: step.secondary_code,
track_reference: step.track_reference,
};
};

export default getStepLocation;
6 changes: 5 additions & 1 deletion front/src/modules/pathfinding/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,11 @@ export const upsertPathStepsInOPs = (ops: SuggestedOP[], pathSteps: PathStep[]):
}
} else {
updatedOPs = updatedOPs.map((op) => {
if (matchPathStepAndOp(step, op) && op.kp === step.kp) {
if (
matchPathStepAndOp(step, op) &&
op.kp === step.kp &&
step.positionOnPath === op.positionOnPath
) {
return {
...op,
pathStepId: step.id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,44 +13,62 @@ 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';
import OperationalPointPopupDetails from './OperationalPointPopupDetails';

type AddPathStepPopupProps = {
pathProperties?: ManageTrainSchedulePathProperties;
featureInfoClick: FeatureInfoClick;
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 [trackOffset, setTrackOffset] = useState(0);

const [getTrackEntity] =
const { getTrackSectionsByIds } = useScenarioContext();

const [clickedOp, setClickedOp] = useState<
PathStep & {
tracks: {
trackName?: string;
coordinates?: number[];
}[];
}
>();
const [selectedTrack, setSelectedTrack] = useState<{
trackName?: string;
coordinates?: number[];
}>();
const [newPathStep, setNewPathStep] = useState<PathStep>();

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 @@ -64,56 +82,133 @@ function AddPathStepPopup({
point(featureInfoClick.coordinates.slice(0, 2)).geometry,
'millimeters'
);
setTrackOffset(offset);

if (!featureInfoClick.feature.properties) return;

const { properties } = featureInfoClick.feature;
setNewPathStep({
id: nextId(),
coordinates: featureInfoClick.coordinates.slice(0, 2),
track: properties.id,
offset: Math.round(offset),
kp: properties.kp,
metadata: {
lineCode: properties.extensions_sncf_line_code,
lineName: properties.extensions_sncf_line_name,
trackName: properties.extensions_sncf_track_name,
trackNumber: properties.extensions_sncf_track_number,
},
});
};

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 (featureInfoClick.isOperationalPoint) {
handleOperationalPoint();
} else {
handleTrack();
}
}, [featureInfoClick]);

if (!featureInfoClick.feature.properties) return null;
useEffect(() => {
if (!clickedOp || !selectedTrack) {
setNewPathStep(undefined);
return;
}

const { tracks: _tracks, ...opWithoutTracks } = clickedOp;
setNewPathStep({
...opWithoutTracks,
coordinates: selectedTrack.coordinates,
track_reference: selectedTrack.trackName
? { track_name: selectedTrack.trackName }
: undefined,
});
}, [clickedOp, selectedTrack]);

if (
!newPathStep ||
!featureInfoClick.feature.properties ||
(featureInfoClick.isOperationalPoint && !clickedOp)
)
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,
},
};

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>
</div>
<div className="details-line">
{featureInfoClick.feature.properties.extensions_sncf_line_name}
{featureInfoClick.isOperationalPoint ? (
<OperationalPointPopupDetails
operationalPoint={featureInfoClick}
clickedOp={clickedOp!}
selectedTrack={selectedTrack!}
setSelectedTrack={setSelectedTrack}
/>
) : (
<div className="details">
<div className="details-track">
{featureInfoClick.feature.properties.extensions_sncf_track_name}
<small>{featureInfoClick.feature.properties.extensions_sncf_line_code}</small>
</div>
<div className="details-line">
{featureInfoClick.feature.properties.extensions_sncf_line_name}
</div>
</div>
</div>
)}

<div className="actions">
<button
data-testid="map-origin-button"
className="btn btn-sm btn-success"
type="button"
onClick={() =>
setPointIti('origin', pathStepProperties, launchPathfinding, resetFeatureInfoClick)
setPointIti('origin', newPathStep, launchPathfinding, resetFeatureInfoClick)
}
>
<RiMapPin2Fill />
Expand All @@ -123,26 +218,25 @@ function AddPathStepPopup({
<button
className="btn btn-sm btn-info"
type="button"
onClick={() =>
onClick={() => {
setPointIti(
'via',
pathStepProperties,
newPathStep,
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={() =>
setPointIti('destination', pathStepProperties, launchPathfinding, resetFeatureInfoClick)
setPointIti('destination', newPathStep, launchPathfinding, resetFeatureInfoClick)
}
>
<IoFlag />
Expand All @@ -151,6 +245,6 @@ function AddPathStepPopup({
</div>
</Popup>
);
}
};

export default React.memo(AddPathStepPopup);
Loading
Loading