Skip to content

Commit e3efd2a

Browse files
committed
front: synchronize speed space and space curves slopes charts
1 parent 96c86f7 commit e3efd2a

File tree

6 files changed

+107
-6
lines changed

6 files changed

+107
-6
lines changed

front/src/applications/operationalStudies/views/SimulationResults.tsx

+27-3
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,15 @@ import SimulationWarpedMap from 'common/Map/WarpedMap/SimulationWarpedMap';
2222
import { osrdEditoastApi, SimulationReport } from 'common/api/osrdEditoastApi';
2323

2424
import SimulationResultsMap from 'modules/simulationResult/components/SimulationResultsMap';
25-
import SpaceCurvesSlopes from 'modules/simulationResult/components/SpaceCurvesSlopes';
26-
import SpaceTimeChartIsolated from 'modules/simulationResult/components/SpaceTimeChart/withOSRDData';
27-
import SpeedSpaceChart from 'modules/simulationResult/components/SpeedSpaceChart/SpeedSpaceChart';
2825
import TimeButtons from 'modules/simulationResult/components/TimeButtons';
2926
// TIMELINE DISABLED // import TimeLine from 'modules/simulationResult/components/TimeLine/TimeLine';
3027
import TrainDetails from 'modules/simulationResult/components/TrainDetails';
3128
import DriverTrainSchedule from 'modules/trainschedule/components/DriverTrainSchedule/DriverTrainSchedule';
29+
import getScaleDomainFromValues from 'modules/simulationResult/components/ChartHelpers/getScaleDomainFromValues';
30+
import SpaceCurvesSlopes from 'modules/simulationResult/components/SpaceCurvesSlopes';
31+
import SpeedSpaceChart from 'modules/simulationResult/components/SpeedSpaceChart/SpeedSpaceChart';
32+
import SpaceTimeChartIsolated from 'modules/simulationResult/components/SpaceTimeChart/withOSRDData';
33+
import { PositionScaleDomain } from 'modules/simulationResult/components/simulationResultsConsts';
3234

3335
const MAP_MIN_HEIGHT = 450;
3436

@@ -62,6 +64,13 @@ export default function SimulationResults({
6264
const [initialHeightOfSpaceCurvesSlopesChart, setInitialHeightOfSpaceCurvesSlopesChart] =
6365
useState(heightOfSpaceCurvesSlopesChart);
6466

67+
// X scale domain shared between SpeedSpace and SpaceCurvesSlopes charts.
68+
const [positionScaleDomain, setPositionScaleDomain] = useState<PositionScaleDomain>({
69+
initial: [],
70+
current: [],
71+
source: undefined,
72+
});
73+
6574
const { data: selectedTrainSchedule } = osrdEditoastApi.endpoints.getTrainScheduleById.useQuery(
6675
{
6776
id: selectedTrain?.id as number,
@@ -106,6 +115,17 @@ export default function SimulationResults({
106115
}
107116
}, [extViewport]);
108117

118+
useEffect(() => {
119+
if (selectedTrain) {
120+
const positions = selectedTrain.base.speeds.map((speed) => speed.position);
121+
const newPositionsScaleDomain = getScaleDomainFromValues(positions);
122+
setPositionScaleDomain({
123+
initial: newPositionsScaleDomain,
124+
current: newPositionsScaleDomain,
125+
});
126+
}
127+
}, [selectedTrain]);
128+
109129
return simulation.trains.length === 0 && !isUpdating ? (
110130
<h1 className="text-center mt-5">{t('noData')}</h1>
111131
) : (
@@ -176,6 +196,8 @@ export default function SimulationResults({
176196
selectedTrain={selectedTrain}
177197
timePosition={timePosition}
178198
trainRollingStock={selectedTrainRollingStock}
199+
sharedXScaleDomain={positionScaleDomain}
200+
setSharedXScaleDomain={setPositionScaleDomain}
179201
/>
180202
</div>
181203
</div>
@@ -210,6 +232,8 @@ export default function SimulationResults({
210232
selectedTrain={selectedTrain}
211233
timePosition={timePosition}
212234
positionValues={positionValues}
235+
sharedXScaleDomain={positionScaleDomain}
236+
setSharedXScaleDomain={setPositionScaleDomain}
213237
/>
214238
</Rnd>
215239
)}

front/src/modules/simulationResult/components/ChartHelpers/enableInteractivity.tsx

+9
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ import {
1212
isSpaceTimeChart,
1313
} from 'modules/simulationResult/components/ChartHelpers/ChartHelpers';
1414
import {
15+
CHART_AXES,
1516
ChartAxes,
1617
LIST_VALUES,
18+
PositionScaleDomain,
1719
} from 'modules/simulationResult/components/simulationResultsConsts';
1820
import drawGuideLines from 'modules/simulationResult/components/ChartHelpers/drawGuideLines';
1921
import { dateIsInRange } from 'utils/date';
@@ -257,6 +259,7 @@ export const enableInteractivity = <
257259
simulationIsPlaying: boolean,
258260
dispatchUpdateTimePositionValues: (newTimePositionValues: Date) => void,
259261
chartDimensions: [Date, Date],
262+
setSharedXScaleDomain?: React.Dispatch<React.SetStateAction<PositionScaleDomain>>,
260263
additionalValues: ChartAxes[] = [] // more values to display on the same chart
261264
) => {
262265
if (!chart) return;
@@ -271,6 +274,12 @@ export const enableInteractivity = <
271274
event.sourceEvent.preventDefault();
272275
const zoomFunctions = updateChart(chart, keyValues, additionalValues, rotate, event);
273276
const newChart = { ...chart, x: zoomFunctions.newX, y: zoomFunctions.newY };
277+
if (setSharedXScaleDomain)
278+
setSharedXScaleDomain((prevState) => ({
279+
...prevState,
280+
current: newChart.x.domain() as number[],
281+
source: keyValues === CHART_AXES.SPACE_SPEED ? 'SpeedSpaceChart' : 'SpaceCurvesSlopes',
282+
}));
274283
setChart(newChart);
275284
})
276285
.filter(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import * as d3 from 'd3';
2+
import { defineLinear } from './ChartHelpers';
3+
4+
const getScaleDomainFromValues = (values: number[]) => {
5+
const maxX = d3.max(values) as number;
6+
const scaleX = defineLinear(maxX + 100);
7+
return scaleX.domain();
8+
};
9+
10+
export default getScaleDomainFromValues;

front/src/modules/simulationResult/components/SpaceCurvesSlopes.tsx

+29-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ import { useDispatch, useSelector } from 'react-redux';
33
import * as d3 from 'd3';
44
import { CgLoadbar } from 'react-icons/cg';
55

6-
import { CHART_AXES } from 'modules/simulationResult/components/simulationResultsConsts';
6+
import {
7+
CHART_AXES,
8+
PositionScaleDomain,
9+
} from 'modules/simulationResult/components/simulationResultsConsts';
710
import {
811
defineLinear,
912
interpolateOnPosition,
@@ -93,19 +96,24 @@ type SpaceCurvesSlopesProps = {
9396
positionValues: PositionsSpeedTimes<Date>;
9497
selectedTrain: Train;
9598
timePosition: Date;
99+
sharedXScaleDomain?: PositionScaleDomain;
100+
setSharedXScaleDomain?: React.Dispatch<React.SetStateAction<PositionScaleDomain>>;
96101
};
97102

98103
const SpaceCurvesSlopes = ({
99104
initialHeight,
100105
positionValues,
101106
selectedTrain,
102107
timePosition,
108+
sharedXScaleDomain,
109+
setSharedXScaleDomain,
103110
}: SpaceCurvesSlopesProps) => {
104111
const dispatch = useDispatch();
105112
const simulationIsPlaying = useSelector(getIsPlaying);
106113

107114
const [chart, setChart] = useState<SpeedSpaceChart | undefined>(undefined);
108115
const [height, setHeight] = useState(initialHeight);
116+
const [initialYScaleDomain, setInitialYScaleDomain] = useState<number[]>([]);
109117

110118
const ref = useRef<HTMLDivElement>(null);
111119
const rotate = false;
@@ -165,6 +173,9 @@ const SpaceCurvesSlopes = ({
165173
const yMax = d3.max(trainData.slopesHistogram, (d) => d.gradient) || 0;
166174
const defineY = chart === undefined ? defineLinear(yMax, 0, yMin) : chart.y;
167175

176+
if (chart === undefined) {
177+
setInitialYScaleDomain(defineY.domain());
178+
}
168179
const width = parseInt(d3.select(`#container-${CHART_ID}`).style('width'), 10);
169180
return defineChart(
170181
width,
@@ -255,8 +266,10 @@ const SpaceCurvesSlopes = ({
255266
simulationIsPlaying,
256267
dispatchUpdateTimePositionValues,
257268
timeScaleRange,
269+
setSharedXScaleDomain,
258270
[CHART_AXES.SPACE_GRADIENT, CHART_AXES.SPACE_RADIUS, CHART_AXES.SPACE_HEIGHT]
259271
);
272+
260273
setChart(chartLocal);
261274
};
262275

@@ -265,7 +278,21 @@ const SpaceCurvesSlopes = ({
265278
setHeight(newHeight);
266279
};
267280

268-
useEffect(() => drawTrain(), [trainData, height]);
281+
useEffect(() => {
282+
drawTrain();
283+
}, [trainData, height]);
284+
285+
useEffect(() => {
286+
if (chart && sharedXScaleDomain && sharedXScaleDomain.source !== CHART_ID) {
287+
const newChart = { ...chart };
288+
newChart.x.domain(sharedXScaleDomain.current);
289+
if (sharedXScaleDomain.initial === sharedXScaleDomain.current) {
290+
newChart.y.domain(initialYScaleDomain);
291+
}
292+
setChart(newChart);
293+
drawTrain();
294+
}
295+
}, [sharedXScaleDomain]);
269296

270297
useEffect(() => setHeight(initialHeight), [initialHeight]);
271298

front/src/modules/simulationResult/components/SpeedSpaceChart/SpeedSpaceChart.tsx

+26-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@ import {
88
enableInteractivity,
99
traceVerticalLine,
1010
} from 'modules/simulationResult/components/ChartHelpers/enableInteractivity';
11-
import { CHART_AXES, ChartAxes } from 'modules/simulationResult/components/simulationResultsConsts';
11+
import {
12+
CHART_AXES,
13+
ChartAxes,
14+
PositionScaleDomain,
15+
} from 'modules/simulationResult/components/simulationResultsConsts';
1216
import {
1317
createChart,
1418
drawTrain,
@@ -46,6 +50,8 @@ export type SpeedSpaceChartProps = {
4650
selectedTrain: SimulationReport | Train;
4751
timePosition: Date;
4852
trainRollingStock?: LightRollingStock;
53+
sharedXScaleDomain?: PositionScaleDomain;
54+
setSharedXScaleDomain?: React.Dispatch<React.SetStateAction<PositionScaleDomain>>;
4955
};
5056

5157
/**
@@ -64,6 +70,8 @@ export default function SpeedSpaceChart({
6470
timePosition,
6571
positionValues,
6672
trainRollingStock,
73+
sharedXScaleDomain,
74+
setSharedXScaleDomain,
6775
}: SpeedSpaceChartProps) {
6876
const simulationIsPlaying = useSelector(getIsPlaying);
6977
const speedSpaceSettings = useSelector(getSpeedSpaceSettings);
@@ -121,6 +129,7 @@ export default function SpeedSpaceChart({
121129
ref,
122130
chart
123131
) as SpeedSpaceChart;
132+
124133
setChart(localChart);
125134
drawTrain(trainSimulation, rotate, localSettings, localChart);
126135
setHasJustRotated(false);
@@ -156,6 +165,15 @@ export default function SpeedSpaceChart({
156165
createChartAndTrain();
157166
}, [rotate, localSettings, chartHeight]);
158167

168+
useEffect(() => {
169+
if (chart && sharedXScaleDomain && sharedXScaleDomain.source !== CHART_ID) {
170+
const newChart = { ...chart };
171+
newChart.x.domain(sharedXScaleDomain.current);
172+
setChart(newChart);
173+
createChartAndTrain();
174+
}
175+
}, [sharedXScaleDomain]);
176+
159177
/**
160178
* reset chart (only if resetChart is true)
161179
*/
@@ -164,6 +182,12 @@ export default function SpeedSpaceChart({
164182
if (rotate) {
165183
// cancel rotation and redraw the train
166184
toggleRotation();
185+
} else if (chart && setSharedXScaleDomain) {
186+
setSharedXScaleDomain((prevState) => ({
187+
...prevState,
188+
current: prevState.initial,
189+
source: undefined,
190+
}));
167191
} else {
168192
createChartAndTrain();
169193
}
@@ -200,6 +224,7 @@ export default function SpeedSpaceChart({
200224
simulationIsPlaying,
201225
dispatchUpdateTimePositionValues,
202226
timeScaleRange,
227+
setSharedXScaleDomain,
203228
additionalAxes
204229
);
205230
}, [chart, additionalAxes]);

front/src/modules/simulationResult/components/simulationResultsConsts.ts

+6
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,9 @@ export type AllListValues = ArrayElement<ListValues>;
4444
export const SIGNAL_BASE_DEFAULT = 'BAL3';
4545

4646
export const LIST_VALUES_SIGNAL_BASE = ['BAL3'];
47+
48+
export type PositionScaleDomain = {
49+
initial: number[];
50+
current: number[];
51+
source?: 'SpeedSpaceChart' | 'SpaceCurvesSlopes';
52+
};

0 commit comments

Comments
 (0)