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 10, 2024
1 parent f354aff commit 8bdbaf3
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 47 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 @@ -12,11 +14,13 @@ import { useSelector } from 'react-redux';
import { editoastToEditorEntity } from 'applications/editor/data/api';
import type { TrackSectionEntity } from 'applications/editor/tools/trackEdition/types';
import { calculateDistanceAlongTrack } from 'applications/editor/tools/utils';
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 @@ -32,13 +36,31 @@ function RenderPopup({ pathProperties }: RenderPopupProps) {
const { getFeatureInfoClick, getInfraID, getOrigin, getDestination } = useOsrdConfSelectors();
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 @@ -50,25 +72,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 @@ -87,48 +142,89 @@ 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.track_name
: 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} / ${
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={() => setPointIti('origin', pathStepProperties, osrdConfActions)}
>
<RiMapPin2Fill />
<span className="d-none">{t('origin')}</span>
</button>
{/* UPDATE FOR ADD OP */}
{origin && destination && (
<button
className="btn btn-sm btn-info"
Expand All @@ -140,7 +236,6 @@ function RenderPopup({ pathProperties }: RenderPopupProps) {
</button>
)}
<button
data-testid="map-destination-button"
className="btn btn-sm btn-warning"
type="button"
onClick={() => setPointIti('destination', pathStepProperties, osrdConfActions)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,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 @@ -142,12 +143,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 @@ -169,13 +170,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 @@ -187,6 +192,7 @@ const Map = ({
},
});
} else {
setOperationnalPointId(undefined);
setSnappedPoint(undefined);
}
};
Expand Down Expand Up @@ -333,6 +339,7 @@ const Map = ({
colors={colors[mapStyle]}
layerOrder={LAYER_GROUPS_ORDER[LAYERS.OPERATIONAL_POINTS.GROUP]}
infraID={infraID}
operationnalPointId={operationnalPointId}
/>
)}

Expand Down
28 changes: 21 additions & 7 deletions front/src/reducers/osrdconf/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,30 @@ export const insertViaFromMap = (
pathProperties: ManageTrainSchedulePathProperties
): OsrdConfState['pathSteps'] => {
// If one of these is missing, via is not valid (it hasn't been added via click on map) and we return the same array
if (!('track' in newVia) || !newVia.coordinates) return pathSteps;
if (
!newVia.coordinates ||
(!('track' in newVia) && !('uic' in newVia) && !('secondary_code' in newVia))
)
return pathSteps;

const origin = pathSteps[0];
const destination = last(pathSteps);
const newStep = {
track: newVia.track,
offset: newVia.offset,
id: newVia.id,
coordinates: newVia.coordinates,
};
let newStep;
if ('track' in newVia) {
newStep = {
track: newVia.track,
offset: newVia.offset,
id: newVia.id,
coordinates: newVia.coordinates,
};
} else if ('uic' in newVia && 'secondary_code' in newVia) {
newStep = {
uic: newVia.uic,
secondary_code: newVia.secondary_code,
id: newVia.id,
coordinates: newVia.coordinates,
};
}

let newViaIndex = -1;
// If origin and destination have already been selected and there is at least a via,
Expand Down
6 changes: 6 additions & 0 deletions front/src/styles/scss/common/map/_popup.scss
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,10 @@
padding: 4px 8px;
}
}
select {
background-color: var(--white);
}
select:focus {
background-color: var(--white);
}
}

0 comments on commit 8bdbaf3

Please sign in to comment.