Skip to content

Commit 1c2fa87

Browse files
nicolaswurtzalexandredamiron
authored andcommitted
Nwz/import trains from graou (#2586)
* front: importGraou: first try * front: importGraou: date & time added * front: GraouImport: working config, working getTrainsList * front: GraouImport: trainslist ok, beginning modal import * front: GraouImport: path complete done * front: GraouImport: pathfinding ok * front: GraouImport: adding global settings from osrdconfig * front: OpenDataImport: switching from Graou to OpenData * front: OpenDataImport: added rollingstockid to result * front: OpenDataImport: it's working ! * front: OpenDataImport: using real external serveur from Graou * front: OpenDataImport: some corrections * front: OpenDataImport: adapt map to maplibre migration * front: OpenDataImport: cleaning timetableselector a little * front: OpenDataImport: direct links to simulation & stdcm after import * front: OpenDataImport: first pass to make pathfinding by OPs trigram working (and it's working !) * front: OpenDataImport: some bugfixes * front: OpenDataImport: some corrections * front: some bugfixes * front: Remove DGEX Solutions mentions * front: fast relooking homepage * front: rework home page * front: opendataimport: bugfix * front: replace png by svg when possible & svgo * front: OpenDataImport: add defaultProps values * front: OpenDataImport: bugfix on state selection for mapStyle * front: OpenDataImport: state selector for timetableID more lightweight
1 parent 6265f26 commit 1c2fa87

File tree

101 files changed

+1870
-1314
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

101 files changed

+1870
-1314
lines changed

front/public/index.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
work correctly both with client-side routing and a non-root public URL.
2424
Learn how to configure a non-root public URL by running `npm run build`.
2525
-->
26-
<title>OSRD - DGEX Solutions</title>
26+
<title>OSRD</title>
2727
</head>
2828
<body>
2929
<noscript>You need to enable JavaScript to run this app.</noscript>

front/public/locales/fr/home.json

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"customget": "Visualisation JSON GET",
3+
"editor": "Éditeur d'infrastructure",
4+
"map": "Cartographie",
5+
"opendataimport": "Importation horaires",
6+
"stdcm": "Sillons de dernière minute",
7+
"timetable": "Grilles horaires"
8+
}

front/public/locales/fr/opendata.json

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"closeOSRDConfig": "Fermer",
3+
"completeTrackSectionID": "Compléter les coordonnées",
4+
"date": "DATE",
5+
"datetime": "Date & horaires",
6+
"endTime": "FIN",
7+
"errorMessages": {
8+
"error": "Une erreur est survenue",
9+
"errorNoDate": "Vous devez renseigner une date.",
10+
"errorNoFrom": "Vous devez renseigner une origine.",
11+
"errorNoTo": "Vous devez renseigner une destination.",
12+
"errorSameFromTo": "L'origine et la destination doivent être différentes.",
13+
"unableToRetrievePathfinding": "Impossible de créer un chemin"
14+
},
15+
"from": "Origine",
16+
"generatePaths": "Générer les pathfinding",
17+
"generatePathsAuto": "Compléter et générer les pathfinding automatiquement",
18+
"generateTrainSchedules": "Générer les calculs de marche",
19+
"goToSimulation": "Résultats de simulation",
20+
"goToSTDCM": "Sillon de dernière minute",
21+
"import": "Importation",
22+
"inputPlaceholder": "Nom, trigramme",
23+
"launchImport": "Démarrer l'importation",
24+
"noResults": "Aucun résultat",
25+
"openOSRDConfig": "Infra / Table horaire / Matériel",
26+
"startTime": "DÉBUT",
27+
"status": {
28+
"calculatingTrainSchedule": "Calcul de marches en cours...",
29+
"calculatingTrainScheduleComplete": "Calcul de marches terminé",
30+
"calculatingTrainScheduleCompleteAll": "Tous les calculs de marches sont terminés.",
31+
"calculatingTrainScheduleError": "Le calcul de marche a échoué",
32+
"complete": "positionnez",
33+
"missingInfra": "Vous devez renseigner une infrastructure",
34+
"missingRollingStock": "Vous devez choisir un matériel roulant par défaut",
35+
"missingTimetable": "Vous devez choisir une grille horaire",
36+
"noImportationPossible": "L'importation n'est pas possible pour l'instant :",
37+
"pathComplete": "Tous les pathfinding ont été effectués",
38+
"ready": "Prêt.",
39+
"searchingPath": "Génération pathfinding",
40+
"uicComplete": "Tous les lieux ont été renseignés."
41+
},
42+
"to": "Destination",
43+
"trainsFound": "trains trouvé(s)"
44+
}

front/public/locales/fr/translation.json

-7
Original file line numberDiff line numberDiff line change
@@ -194,13 +194,6 @@
194194
"track_section-length": "Taille"
195195
}
196196
},
197-
"Home": {
198-
"customget": "Visualisation JSON GET",
199-
"map": "Cartographie",
200-
"editor": "Éditeur d'infrastructure",
201-
"timetable": "Grilles horaires",
202-
"stdcm": "Sillons de dernière minute (en travaux)"
203-
},
204197
"Login": {
205198
"connect": "Se connecter",
206199
"englishFlag": "drapeau anglais",

front/src/applications/carto/Map.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ function Map() {
125125
touchZoomRotate
126126
>
127127
<VirtualLayers />
128-
<AttributionControl customAttribution="©SNCF/DGEX Solutions" />
128+
<AttributionControl customAttribution="©SNCF Réseau" />
129129
<ScaleControl maxWidth={100} unit="metric" style={scaleControlStyle} />
130130

131131
<Background

front/src/applications/customget/views/SpaceTimeChart.js

+6
Original file line numberDiff line numberDiff line change
@@ -179,11 +179,13 @@ export default function SpaceTimeChart(props) {
179179
setTimeout(() => {
180180
dispatch(updateMustRedraw(true));
181181
}, 0);
182+
// eslint-disable-next-line react-hooks/exhaustive-deps
182183
}, []);
183184

184185
useEffect(() => {
185186
// ADN, entire fonction operation is subject to one condition, so aopply this condition before OR write clear and first condition to return (do nothing)
186187
offsetTimeByDragging(dragOffset);
188+
// eslint-disable-next-line react-hooks/exhaustive-deps
187189
}, [dragOffset]);
188190

189191
useEffect(() => {
@@ -196,6 +198,7 @@ export default function SpaceTimeChart(props) {
196198
drawAllTrains(resetChart);
197199
handleWindowResize(CHART_ID, dispatch, drawAllTrains, isResizeActive, setResizeActive);
198200
}
201+
// eslint-disable-next-line react-hooks/exhaustive-deps
199202
}, [mustRedraw, rotate, selectedTrain, consolidatedSimulation]);
200203

201204
// ADN: trigger a redraw on every simulation change. This is the right pattern.
@@ -208,6 +211,7 @@ export default function SpaceTimeChart(props) {
208211
drawAllTrains(resetChart, true, newDataSimulation);
209212
handleWindowResize(CHART_ID, dispatch, drawAllTrains, isResizeActive, setResizeActive);
210213
}
214+
// eslint-disable-next-line react-hooks/exhaustive-deps
211215
}, [simulation.trains]);
212216

213217
useEffect(() => {
@@ -221,6 +225,7 @@ export default function SpaceTimeChart(props) {
221225
);
222226
dispatch(updatePositionValues(newPositionValues));
223227
}
228+
// eslint-disable-next-line react-hooks/exhaustive-deps
224229
}, [chart, mustRedraw]);
225230

226231
useEffect(() => {
@@ -236,6 +241,7 @@ export default function SpaceTimeChart(props) {
236241
timePosition
237242
);
238243
}
244+
// eslint-disable-next-line react-hooks/exhaustive-deps
239245
}, [positionValues]);
240246

241247
useEffect(() => {

front/src/applications/customget/views/TrainList.js

+3
Original file line numberDiff line numberDiff line change
@@ -163,19 +163,22 @@ export default function TrainsList() {
163163
setOnInput(false);
164164
dispatch(updateMustRedraw(true));
165165
}
166+
// eslint-disable-next-line react-hooks/exhaustive-deps
166167
}, [debouncedInputName]);
167168

168169
useEffect(() => {
169170
if (debouncedInputTime) {
170171
setOnInput(false);
171172
dispatch(updateMustRedraw(true));
172173
}
174+
// eslint-disable-next-line react-hooks/exhaustive-deps
173175
}, [debouncedInputTime]);
174176

175177
useEffect(() => {
176178
if (!onInput) {
177179
setFormattedList(formatTrainsList());
178180
}
181+
// eslint-disable-next-line react-hooks/exhaustive-deps
179182
}, [selectedTrain, departureArrivalTimes, trainNameClickedIDX, typeOfInputFocused]);
180183

181184
return (

front/src/applications/editor/Map.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ const MapUnplugged: FC<PropsWithChildren<MapProps>> = ({
172172
}}
173173
>
174174
<VirtualLayers />
175-
<AttributionControl position="bottom-right" customAttribution="©SNCF/DGEX Solutions" />
175+
<AttributionControl position="bottom-right" customAttribution="©SNCF Réseau" />
176176
<ScaleControl
177177
maxWidth={100}
178178
unit="metric"
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import React from 'react';
2+
import { Navigate, Route, Routes } from 'react-router-dom';
3+
import MastNavItemSNCF from 'common/BootstrapSNCF/MastNavItemSNCF';
4+
import MastNavSNCF from 'common/BootstrapSNCF/MastNavSNCF';
5+
import NavBarSNCF from 'common/BootstrapSNCF/NavBarSNCF';
6+
import { NotificationsState } from 'common/Notifications';
7+
import { useTranslation } from 'react-i18next';
8+
import { MdMoreTime } from 'react-icons/md';
9+
import logo from 'assets/logo_osrd_seul_blanc.svg';
10+
import './opendata.scss';
11+
import OpenDataImport from './OpenDataImport';
12+
13+
export default function HomeOpenData() {
14+
const { t } = useTranslation(['opendata']);
15+
return (
16+
<>
17+
<MastNavSNCF
18+
items={
19+
<MastNavItemSNCF link="/opendata/import" linkname={t('import')} icon={<MdMoreTime />} />
20+
}
21+
/>
22+
<NavBarSNCF appName="OSRD / Open data" logo={logo} />
23+
<Routes>
24+
<Route path="/import" element={<OpenDataImport />} />
25+
<Route path="" element={<Navigate to="/opendata/import" replace />} />
26+
</Routes>
27+
<NotificationsState />
28+
</>
29+
);
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import React, { useEffect, useState } from 'react';
2+
import OpenDataImportConfig from 'applications/opendata/views/OpenDataImportConfig';
3+
import OpenDataTrainsList from 'applications/opendata/views/OpenDataTrainsList';
4+
import OpenDataGlobalSettings from 'applications/opendata/views/OpenDataGlobalSettings';
5+
import { get } from 'common/requests';
6+
import Loader from 'common/Loader';
7+
8+
const ROLLING_STOCK_URL = '/light_rolling_stock/';
9+
10+
export default function OpenDataImport() {
11+
const [config, setConfig] = useState();
12+
const [rollingStockDB, setRollingStockDB] = useState();
13+
const [mustUpdateTimetable, setMustUpdateTimetable] = useState(true);
14+
15+
async function getRollingStockDB() {
16+
try {
17+
const data = await get(ROLLING_STOCK_URL, { page_size: 1000 });
18+
setRollingStockDB(data.results);
19+
} catch (error) {
20+
console.log(error);
21+
}
22+
}
23+
24+
useEffect(() => {
25+
if (!rollingStockDB) {
26+
getRollingStockDB();
27+
}
28+
// eslint-disable-next-line react-hooks/exhaustive-deps
29+
}, []);
30+
31+
return rollingStockDB ? (
32+
<main className="osrd-config-mastcontainer mastcontainer opendata-import">
33+
<div className="p-3">
34+
<OpenDataImportConfig setConfig={setConfig} />
35+
<OpenDataGlobalSettings
36+
mustUpdateTimetable={mustUpdateTimetable}
37+
setMustUpdateTimetable={setMustUpdateTimetable}
38+
/>
39+
<OpenDataTrainsList
40+
config={config}
41+
rollingStockDB={rollingStockDB}
42+
setMustUpdateTimetable={setMustUpdateTimetable}
43+
/>
44+
</div>
45+
</main>
46+
) : (
47+
<Loader />
48+
);
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import React, { useEffect, useRef, useState } from 'react';
2+
import PropTypes from 'prop-types';
3+
import maplibregl from 'maplibre-gl';
4+
import ReactMapGL, { AttributionControl, ScaleControl } from 'react-map-gl';
5+
import { point as turfPoint } from '@turf/helpers';
6+
import { useSelector } from 'react-redux';
7+
import turfNearestPointOnLine from '@turf/nearest-point-on-line';
8+
9+
/* Main data & layers */
10+
import Background from 'common/Map/Layers/Background';
11+
import VirtualLayers from 'applications/osrd/views/OSRDSimulation/VirtualLayers';
12+
import SnappedMarker from 'common/Map/Layers/SnappedMarker';
13+
/* Objects & various */
14+
import TracksGeographic from 'common/Map/Layers/TracksGeographic';
15+
import colors from 'common/Map/Consts/colors';
16+
import osmBlankStyle from 'common/Map/Layers/osmBlankStyle';
17+
import { LAYER_GROUPS_ORDER, LAYERS } from 'config/layerOrder';
18+
19+
import 'common/Map/Map.scss';
20+
import OperationalPoints from 'common/Map/Layers/OperationalPoints';
21+
import Platforms from 'common/Map/Layers/Platforms';
22+
import { getMapMouseEventNearestFeature } from 'utils/mapboxHelper';
23+
24+
export default function Map(props) {
25+
const { viewport, setViewport, setClickedFeature } = props;
26+
const mapStyle = useSelector((state) => state.map.mapStyle);
27+
const [lngLatHover, setLngLatHover] = useState();
28+
const [trackSectionGeoJSON, setTrackSectionGeoJSON] = useState();
29+
const [snappedPoint, setSnappedPoint] = useState();
30+
const mapRef = useRef(null);
31+
const scaleControlStyle = {
32+
left: 20,
33+
bottom: 20,
34+
};
35+
36+
const onFeatureClick = (e) => {
37+
const result = getMapMouseEventNearestFeature(e);
38+
if (
39+
result &&
40+
result.feature.properties &&
41+
result.feature.properties.id &&
42+
result.feature.geometry.type === 'LineString'
43+
) {
44+
setClickedFeature(result.feature);
45+
}
46+
};
47+
48+
const onMoveGetFeature = (e) => {
49+
const result = getMapMouseEventNearestFeature(e);
50+
if (
51+
result &&
52+
result.feature.properties &&
53+
result.feature.properties.id &&
54+
result.feature.geometry.type === 'LineString'
55+
) {
56+
setTrackSectionGeoJSON(result.feature.geometry);
57+
setLngLatHover(result.nearest);
58+
} else {
59+
setSnappedPoint(undefined);
60+
}
61+
};
62+
63+
useEffect(() => {
64+
if (trackSectionGeoJSON !== undefined && lngLatHover !== undefined) {
65+
const point = turfPoint(lngLatHover);
66+
try {
67+
setSnappedPoint(turfNearestPointOnLine(trackSectionGeoJSON, point));
68+
} catch (error) {
69+
console.warn(`Ìmpossible to snapPoint - error ${error}`);
70+
}
71+
}
72+
}, [trackSectionGeoJSON, lngLatHover]);
73+
74+
return (
75+
<ReactMapGL
76+
ref={mapRef}
77+
{...viewport}
78+
style={{ cursor: 'pointer' }}
79+
width="100%"
80+
height="100%"
81+
mapLib={maplibregl}
82+
mapStyle={osmBlankStyle}
83+
onMove={(e) => setViewport(e.viewState)}
84+
onMouseMove={(e) => onMoveGetFeature(e)}
85+
attributionControl={false} // Defined below
86+
onClick={onFeatureClick}
87+
interactiveLayerIds={['chartis/tracks-geo/main']}
88+
touchZoomRotate
89+
>
90+
<VirtualLayers />
91+
<AttributionControl className="attribution-control" customAttribution="©SNCF Réseau" />
92+
<ScaleControl maxWidth={100} unit="metric" style={scaleControlStyle} />
93+
94+
<Background
95+
colors={colors[mapStyle]}
96+
layerOrder={LAYER_GROUPS_ORDER[LAYERS.BACKGROUND.GROUP]}
97+
/>
98+
99+
<Platforms
100+
colors={colors[mapStyle]}
101+
layerOrder={LAYER_GROUPS_ORDER[LAYERS.PLATFORMS.GROUP]}
102+
/>
103+
104+
<TracksGeographic
105+
colors={colors[mapStyle]}
106+
layerOrder={LAYER_GROUPS_ORDER[LAYERS.TRACKS_GEOGRAPHIC.GROUP]}
107+
/>
108+
<OperationalPoints
109+
geomType="geo"
110+
colors={colors[mapStyle]}
111+
layerOrder={LAYER_GROUPS_ORDER[LAYERS.OPERATIONAL_POINTS.GROUP]}
112+
/>
113+
{snappedPoint !== undefined ? <SnappedMarker geojson={snappedPoint} /> : null}
114+
</ReactMapGL>
115+
);
116+
}
117+
118+
Map.propTypes = {
119+
viewport: PropTypes.object.isRequired,
120+
setViewport: PropTypes.func.isRequired,
121+
setClickedFeature: PropTypes.func.isRequired,
122+
};

0 commit comments

Comments
 (0)