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 9, 2025
1 parent 46bea7d commit fe37836
Show file tree
Hide file tree
Showing 12 changed files with 298 additions and 70 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
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;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const OPERATIONAL_POINT_LAYERS = [
'chartis/osrd_operational_point/geo',
'chartis/osrd_operational_point_yardname/geo',
'chartis/osrd_operational_point_name_short/geo',
'chartis/osrd_operational_point_name/geo',
'chartis/osrd_operational_point_kp/geo',
];

export default OPERATIONAL_POINT_LAYERS;
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

0 comments on commit fe37836

Please sign in to comment.