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: Pathfinding module #2854

Merged
merged 55 commits into from
Jan 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
476d066
pathfinding api
anisometropie Jan 11, 2023
285e001
sanitize featureInfoClick data in store
anisometropie Jan 11, 2023
33fae26
Merge remote-tracking branch 'origin/dev' into vcs/pathfinding-module
anisometropie Jan 11, 2023
6e9df2b
isolate & refactor pathfinding behavior
anisometropie Jan 12, 2023
f315c92
fix types
anisometropie Jan 12, 2023
87b48d3
Merge remote-tracking branch 'origin/dev' into vcs/pathfinding-module
anisometropie Jan 13, 2023
9f74b4f
fix types
anisometropie Jan 13, 2023
cd1c385
map selectors
anisometropie Jan 13, 2023
4f88197
refactor selectors
anisometropie Jan 13, 2023
543140e
better selector types
anisometropie Jan 16, 2023
1ff6f1c
fix path types
anisometropie Jan 16, 2023
1296799
openapi
anisometropie Jan 16, 2023
94fc88e
use mapTrackSources
anisometropie Jan 17, 2023
dd798c0
Merge remote-tracking branch 'origin/dev' into vcs/pathfinding-module
anisometropie Jan 17, 2023
64a3e45
Merge remote-tracking branch 'origin/dev' into vcs/pathfinding-module
anisometropie Jan 17, 2023
3300dbf
Merge remote-tracking branch 'origin/dev' into vcs/pathfinding-module
anisometropie Jan 17, 2023
050967d
Merge remote-tracking branch 'origin/dev' into vcs/pathfinding-module
anisometropie Jan 18, 2023
000fcdb
Merge remote-tracking branch 'origin/dev' into vcs/pathfinding-module
anisometropie Jan 18, 2023
0292ea9
configure the 3 services
anisometropie Jan 18, 2023
872ef1a
fix pathfinding api response
anisometropie Jan 18, 2023
a18b4d6
yarn
anisometropie Jan 18, 2023
f02daf8
delete no longer needed
anisometropie Jan 18, 2023
44f9a0e
fix types
anisometropie Jan 19, 2023
64cc983
pathfinding logic
anisometropie Jan 19, 2023
c85914a
Merge remote-tracking branch 'origin/dev' into vcs/pathfinding-module
anisometropie Jan 19, 2023
bc0f64b
conditionalStringConcat
anisometropie Jan 21, 2023
23e3e56
Show status
anisometropie Jan 21, 2023
6246022
add osrd api middleware to store config
anisometropie Jan 21, 2023
3b3335f
pathfinding in progress loading
anisometropie Jan 23, 2023
b1a1de0
fix spaces
anisometropie Jan 23, 2023
4284f93
fix margin
anisometropie Jan 23, 2023
0c96bb8
Merge remote-tracking branch 'origin/dev' into vcs/pathfinding-module
anisometropie Jan 23, 2023
b798c00
Only one message at a time
anisometropie Jan 23, 2023
5f5e49f
fix overlay
anisometropie Jan 23, 2023
dada07a
translate core error messages
anisometropie Jan 23, 2023
9d72554
Merge remote-tracking branch 'origin/dev' into vcs/pathfinding-module
anisometropie Jan 24, 2023
a97bab8
fix import/path
anisometropie Jan 24, 2023
e6b3e23
cancel button
anisometropie Jan 25, 2023
d5f8f5d
cancel behavior
anisometropie Jan 25, 2023
17c775b
fix types
anisometropie Jan 25, 2023
81b3abe
display distance
anisometropie Jan 25, 2023
d7d00a2
relaunch manually
anisometropie Jan 25, 2023
9136b44
delete logs
anisometropie Jan 26, 2023
300abad
color variables
anisometropie Jan 26, 2023
dbac1f8
move scss to /styles
anisometropie Jan 26, 2023
56787ab
better translations, variables
anisometropie Jan 26, 2023
1215172
fix init logic
anisometropie Jan 26, 2023
e1fa232
margin
anisometropie Jan 26, 2023
8601b0d
move to scss folder
anisometropie Jan 26, 2023
bc91cfb
delete unused variables
anisometropie Jan 26, 2023
bf3a910
fix storybook
anisometropie Jan 26, 2023
f1534e5
Merge remote-tracking branch 'origin/dev' into vcs/pathfinding-module
anisometropie Jan 26, 2023
e9fa4d9
Merge remote-tracking branch 'origin/dev' into vcs/pathfinding-module
anisometropie Jan 26, 2023
2e7a6b6
change vias behavior as it was
anisometropie Jan 26, 2023
df7a702
translation
anisometropie Jan 26, 2023
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
28 changes: 24 additions & 4 deletions api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -191,9 +191,7 @@ paths:
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Path"
$ref: "#/components/schemas/Path"
/pathfinding/op/:
post:
tags:
Expand Down Expand Up @@ -966,7 +964,9 @@ components:
items: { type: number, format: float }
minItems: 2
maxItems: 2
mechanical_energy_consumed: { type: number, format: float }
mechanical_energy_consumed:
type: number
format: float
TrainScheduleResult:
properties:
id:
Expand Down Expand Up @@ -1158,9 +1158,29 @@ components:
geographic:
type: object
description: GeoJson format
properties:
coordinates:
type: array
items:
type: array
items:
type: number
format: float
type:
type: string
schematic:
type: object
description: GeoJson format
properties:
coordinates:
type: array
items:
type: array
items:
type: number
format: float
type:
type: string
slopes:
description: Slopes on the path
type: array
Expand Down
1 change: 0 additions & 1 deletion front/.storybook/preview.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import '../src/styles/styles.scss';
import('@sncf/bootstrap-sncf.metier.reseau/dist/css/bootstrap-sncf.min.css');
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,23 @@
"stdcmError": "Erreur de calcul Sillon de dernière minute",
"The requested train couldn't reach its destination": "Le train ne peut pas atteindre sa destination",
"trainScheduleTitle": "Une erreur est survenue",
"unableToRetrievePath": "Impossible de récupérer un chemin",
"unableToRetrievePathfinding": "Impossible de tracer un chemin",
"unableToRetrievePath": "Impossible de récupérer un itinéraire",
"unableToRetrievePathfinding": "Impossible de tracer un itinéraire",
"unableToRetrieveRollingStock": "Récupération matériel impossible",
"unableToRetrieveTags": "Impossible de récupérer les étiquettes",
"unableToRetrieveTimetable": "Récupération grille horaire impossible",
"unableToRetrieveTimetableList": "Récupération liste de grilles horaires impossible"
},
"pathfindingStatus": "Statut recherche d’itinéraire",
"pathfindingInProgress": "Recherche d’itinéraire en cours",
"pathfindingError": "Erreur dans la recherche d’itinéraire : {{errorMessage}}",
"pathfindingDone": "Recherche terminée : ",
"pathfindingMissingParams": "Éléments manquants pour la recherche : {{missingElements}}",
"No path could be found": "Aucun itinéraire trouvé",
"No path could be found after loading Gauge constraints": "Aucun itinéraire trouvé avec ce gabarit de matériel",
"No path could be found after loading Electrification constraints": "Aucun itinéraire trouvé pour un matériel avec ce type d’électrification",
"cancelPathfinding": "Annuler",
"restartPathfinding": "Relancer la recherche",
"gare": "Gare, poste",
"inverseOD": "Inverser origine/destination",
"indications": {
Expand All @@ -41,7 +51,6 @@
"noTimetable": "Choisissez une grille horaire",
"noTrainCompo": "Aucun matériel défini…",
"origin": "Origine",
"pathFindingInProgress": "Récupération du chemin en cours",
"pleaseWait": "Veuillez patientez",
"rollingstock": "Matériel",
"searchingItinerary": "Recherche de sillon…",
Expand Down
7 changes: 4 additions & 3 deletions front/src/Store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import {
legacy_createStore as createStore,
combineReducers,
} from 'redux';
import thunk from 'redux-thunk';
import { persistStore } from 'redux-persist';
import { composeWithDevTools, Config } from '@redux-devtools/extension';

import { persistStore } from 'redux-persist';
import { osrdMiddlewareApi } from 'common/api/osrdMiddlewareApi';
import persistedReducer, { rootReducer, rootInitialState, RootState } from 'reducers';
import thunk from 'redux-thunk';

const reduxDevToolsOptions: Config = {
serialize: {
Expand All @@ -21,7 +22,7 @@ const reduxDevToolsOptions: Config = {
// const composeEnhancers = composeWithDevToolsLogOnlyInProduction(reduxDevToolsOptions);
const composeEnhancers = composeWithDevTools(reduxDevToolsOptions) || compose;

const enhancers = composeEnhancers(applyMiddleware(thunk));
const enhancers = composeEnhancers(applyMiddleware(thunk, osrdMiddlewareApi.middleware));
const store = createStore(persistedReducer, enhancers);

const persistor = persistStore(store);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,37 +1,21 @@
import React, { useEffect, useState } from 'react';
import {
replaceVias,
updateDestination,
updateItinerary,
updateOrigin,
updatePathfindingID,
updateSuggeredVias,
} from 'reducers/osrdconf';
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';

import DisplayItinerary from 'applications/operationalStudies/components/ManageTrainSchedule/Itinerary/DisplayItinerary';
import ModalSugerredVias from 'applications/operationalStudies/components/ManageTrainSchedule/Itinerary/ModalSuggeredVias';
import PropTypes from 'prop-types';
import DotsLoader from 'common/DotsLoader/DotsLoader';
import { WebMercatorViewport } from 'viewport-mercator-project';
import bbox from '@turf/bbox';
import { post } from 'common/requests';
import { setFailure } from 'reducers/main';

import { replaceVias, updateDestination, updateOrigin } from 'reducers/osrdconf';
import { updateFeatureInfoClick } from 'reducers/map';
import { useTranslation } from 'react-i18next';
import { getInfraID } from 'reducers/osrdconf/selectors';

const itineraryURI = '/pathfinding/';
import DisplayItinerary from 'applications/operationalStudies/components/ManageTrainSchedule/Itinerary/DisplayItinerary';
import ModalSugerredVias from 'applications/operationalStudies/components/ManageTrainSchedule/Itinerary/ModalSuggeredVias';
import { getInfraID } from 'reducers/osrdconf/selectors';

function Itinerary(props) {
const [launchPathfinding, setLaunchPathfinding] = useState(false);
const [pathfindingInProgress, setPathfindingInProgress] = useState(false);
const { vias } = useSelector((state) => state.osrdconf);
const { updateExtViewport } = props;
const dispatch = useDispatch();
const map = useSelector((state) => state.map);
const osrdconf = useSelector((state) => state.osrdconf);
const { t } = useTranslation(['operationalStudies/manageTrainSchedule']);
const infra = useSelector(getInfraID);

const zoomToFeature = (boundingBox, id = undefined, source = undefined) => {
Expand Down Expand Up @@ -75,130 +59,10 @@ function Itinerary(props) {
}
};

// Obtain only asked vias
const convertPathfindingVias = (steps, idxToAdd) => {
const count = steps.length - 1;
const newVias = [];
steps.forEach((step, idx) => {
if (idx !== 0 && idx !== count && (!step.suggestion || idxToAdd === idx)) {
newVias.push({
...step,
id: step.position,
clickLngLat: [
step[map.mapTrackSources.substr(0, 3)].coordinates[0],
step[map.mapTrackSources.substr(0, 3)].coordinates[1],
],
});
}
});
dispatch(replaceVias(newVias));
dispatch(updateSuggeredVias(steps));
};

const removeViaFromPath = (step) => {
dispatch(replaceVias(vias.filter((via) => via.track !== step.track)));
};

// Way to ensure marker position on track
const correctWaypointsGPS = (pathfindingData) => {
setLaunchPathfinding(false);
dispatch(
updateOrigin({
...osrdconf.origin,
clickLngLat: pathfindingData.steps[0][map.mapTrackSources.substr(0, 3)].coordinates,
})
);

if (osrdconf.vias.length > 0 || pathfindingData.steps.length > 2) {
convertPathfindingVias(pathfindingData.steps);
}

dispatch(
updateDestination({
...osrdconf.destination,
clickLngLat:
pathfindingData.steps[pathfindingData.steps.length - 1][map.mapTrackSources.substr(0, 3)]
.coordinates,
})
);
setLaunchPathfinding(true);
};

const postPathFinding = async (zoom, params) => {
try {
setPathfindingInProgress(true);
const itineraryCreated = await post(itineraryURI, params, {}, true);
correctWaypointsGPS(itineraryCreated);
dispatch(updateItinerary(itineraryCreated));
dispatch(updatePathfindingID(itineraryCreated.id));
if (zoom) zoomToFeature(bbox(itineraryCreated[map.mapTrackSources]));
setPathfindingInProgress(false);
} catch (e) {
dispatch(
setFailure({
name: t('errorMessages.unableToRetrievePathfinding'),
message: `${e.message} : ${e.response && e.response.data.detail}`,
})
);
console.log('ERROR', e);
}
};

const mapItinerary = (zoom = true) => {
dispatch(updateItinerary(undefined));

if (
osrdconf.origin !== undefined &&
osrdconf.destination !== undefined &&
osrdconf.rollingStockID !== undefined
) {
const params = {
infra: osrdconf.infraID,
steps: [],
rolling_stocks: [osrdconf.rollingStockID],
};

// Adding start point
params.steps.push({
duration: 0,
waypoints: [
{
track_section: osrdconf.origin.id,
geo_coordinate: osrdconf.origin.clickLngLat,
},
],
});

// Adding via points if exist
if (osrdconf.vias.length > 0) {
osrdconf.vias.forEach((via) => {
params.steps.push({
duration: via.duration === undefined ? 0 : parseInt(via.duration, 10),
waypoints: [
{
track_section: via.track || via.id,
geo_coordinate: via.clickLngLat,
},
],
});
});
}

// Adding end point
params.steps.push({
duration: 1,
waypoints: [
{
track_section: osrdconf.destination.id,
geo_coordinate: osrdconf.destination.clickLngLat,
},
],
});

postPathFinding(zoom, params);
}
};

const inverseOD = () => {
if (osrdconf.origin && osrdconf.destination) {
const origin = { ...osrdconf.origin };
Expand All @@ -208,69 +72,30 @@ function Itinerary(props) {
const newVias = Array.from(osrdconf.vias);
dispatch(replaceVias(newVias.reverse()));
}
setLaunchPathfinding(true);
}
};

const removeAllVias = () => {
dispatch(replaceVias([]));
setLaunchPathfinding(true);
};

const viaModalContent = (
<ModalSugerredVias
convertPathfindingVias={convertPathfindingVias}
inverseOD={inverseOD}
removeAllVias={removeAllVias}
pathfindingInProgress={pathfindingInProgress}
removeViaFromPath={removeViaFromPath}
/>
);

useEffect(() => {
if (
osrdconf?.pathfindingID === undefined ||
osrdconf?.geojson?.[map?.mapTrackSources] === undefined
) {
mapItinerary();
} else {
zoomToFeature(bbox(osrdconf.geojson[map.mapTrackSources]));
}
setLaunchPathfinding(true);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

useEffect(() => {
if (launchPathfinding) {
mapItinerary();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [osrdconf.origin, osrdconf.destination, map.mapTrackSources, osrdconf.rollingStockID]);

useEffect(() => {
if (launchPathfinding) {
mapItinerary(false);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [osrdconf.vias]);

useEffect(() => {
setPathfindingInProgress(false);
}, [infra]);

return (
<div className="osrd-config-item mb-2">
<div className="osrd-config-item-container" data-testid="itinerary">
<DisplayItinerary
data-testid="display-itinerary"
zoomToFeaturePoint={zoomToFeaturePoint}
zoomToFeature={zoomToFeature}
viaModalContent={viaModalContent}
/>
{pathfindingInProgress && (
<div className="osrd-config-centered-item">
<DotsLoader /> {`${t('pathFindingInProgress')}`}
</div>
)}
</div>
</div>
);
Expand Down
Loading