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 Dec 17, 2024
1 parent 5d127e0 commit b4648bb
Show file tree
Hide file tree
Showing 5 changed files with 213 additions and 51 deletions.
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
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* 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 { useTranslation } from 'react-i18next';
import { IoFlag } from 'react-icons/io5';
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 { useOsrdConfActions, 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';

type FeatureInfoClickType = {
displayPopup: boolean;
Expand All @@ -34,13 +38,31 @@ function RenderPopup({ pathProperties }: RenderPopupProps) {
const { launchPathfinding } = useManageTrainScheduleContext();
const osrdConfActions = useOsrdConfActions();
const { t } = useTranslation(['operationalStudies/manageTrainSchedule']);

const featureInfoClick: FeatureInfoClickType = useSelector(getFeatureInfoClick);
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<
PathStep & {
tracks: {
trackName?: string;
coordinates?: number[];
isSelected: boolean;
}[];
}
>();

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

Expand All @@ -52,25 +74,58 @@ function RenderPopup({ pathProperties }: RenderPopupProps) {
!featureInfoClick.coordinates
)
return;
const trackId = featureInfoClick.feature.properties.id;

const objectId = featureInfoClick.feature.properties.id;

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

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

const trackEntity = editoastToEditorEntity<TrackSectionEntity>(result[0], 'TrackSection');
const offset = calculateDistanceAlongTrack(
trackEntity,
point(featureInfoClick.coordinates.slice(0, 2)).geometry,
'millimeters'
);
setTrackOffset(offset);
if (isOperationalPoint) {
const trackId = featureInfoClick.feature.properties.track_id;
const clickedTrack = await getTrackEntity({
infraId: infraId!,
objectType: 'TrackSection',
body: [trackId],
}).unwrap();

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

const trackPartCoordinates = parts.map((step) => {
const track = tracks[step.track];
return {
trackName: track.extensions?.sncf?.track_name as string,
coordinates: getPointCoordinates(track.geo, track.length, step.position),
isSelected: step.track === clickedTrack[0].obj_id,
};
});

setClickedOp({
id: nextId(),
secondary_code: result[0].railjson.extensions.sncf.ch,
uic: result[0].railjson.extensions.identifier.uic,
tracks: trackPartCoordinates,
});
} else {
setClickedOp(undefined);
// if operationnalPoint we already have coordinates
const trackEntity = editoastToEditorEntity<TrackSectionEntity>(result[0], 'TrackSection');
const offset = calculateDistanceAlongTrack(
trackEntity,
point(featureInfoClick.coordinates.slice(0, 2)).geometry,
'millimeters'
);
setTrackOffset(offset);
}
};

if (featureInfoClick.displayPopup) {
Expand All @@ -89,41 +144,82 @@ function RenderPopup({ pathProperties }: RenderPopupProps) {
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) {
pathStepProperties = {
...clickedOp,
};
} 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,
},
} as PathStep;
}

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 || ''}
getOptionValue={(option) => option?.trackName || ''}
id="select-track"
onChange={(selectedOption) => {
const updatedTracks = clickedOp.tracks.map((track) => {
if (track.trackName === selectedOption?.trackName) {
return {
...track,
isSelected: true,
};
}
if (track.isSelected) {
return {
...track,
isSelected: false,
};
}
return track;
});
setClickedOp({ ...clickedOp, tracks: updatedTracks });
}}
options={clickedOp.tracks}
value={clickedOp.tracks.find((track) => track.isSelected === true) || clickedOp.tracks[0]}
/>
)}

<div className="actions">
<button
data-testid="map-origin-button"
className="btn btn-sm btn-success"
type="button"
onClick={() =>
Expand Down Expand Up @@ -152,7 +248,6 @@ function RenderPopup({ pathProperties }: RenderPopupProps) {
</button>
)}
<button
data-testid="map-destination-button"
className="btn btn-sm btn-warning"
type="button"
onClick={() =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ const Map = ({

const [mapIsLoaded, setMapIsLoaded] = useState(false);

const [operationnalPointId, setOperationnalPointId] = useState<string | undefined>(undefined);
const [snappedPoint, setSnappedPoint] = useState<Feature<Point> | undefined>();
const { urlLat = '', urlLon = '', urlZoom = '', urlBearing = '', urlPitch = '' } = useParams();
const dispatch = useAppDispatch();
Expand Down Expand Up @@ -143,12 +144,12 @@ const Map = ({

const onFeatureClick = (e: MapLayerMouseEvent) => {
if (preventPointSelection) return;
const result = getMapMouseEventNearestFeature(e, { layersId: ['chartis/tracks-geo/main'] });
const result = getMapMouseEventNearestFeature(e);
if (
result &&
result.feature.properties &&
result.feature.properties.id &&
result.feature.geometry.type === 'LineString'
(result.feature.geometry.type === 'LineString' || result.feature.geometry.type === 'Point')
) {
dispatch(
updateFeatureInfoClick({
Expand All @@ -170,13 +171,17 @@ const Map = ({

const onMoveGetFeature = (e: MapLayerMouseEvent) => {
if (preventPointSelection) return;
const result = getMapMouseEventNearestFeature(e, { layersId: ['chartis/tracks-geo/main'] });
const result = getMapMouseEventNearestFeature(e);
if (
result &&
result.feature.properties &&
result.feature.properties.id &&
result.feature.geometry.type === 'LineString'
(result.feature.geometry.type === 'LineString' || result.feature.geometry.type === 'Point')
) {
if (result.feature.geometry.type === 'Point') {
setOperationnalPointId(result.feature.properties.id);
}

setSnappedPoint({
type: 'Feature',
geometry: {
Expand All @@ -188,6 +193,7 @@ const Map = ({
},
});
} else {
setOperationnalPointId(undefined);
setSnappedPoint(undefined);
}
};
Expand Down Expand Up @@ -338,6 +344,7 @@ const Map = ({
colors={colors[mapStyle]}
layerOrder={LAYER_GROUPS_ORDER[LAYERS.OPERATIONAL_POINTS.GROUP]}
infraID={infraID}
operationnalPointId={operationnalPointId}
/>
)}

Expand Down
Loading

0 comments on commit b4648bb

Please sign in to comment.