Skip to content

Commit 51516f0

Browse files
committed
front: fix ponctual objects position computing
1 parent 5332a7c commit 51516f0

File tree

4 files changed

+95
-40
lines changed

4 files changed

+95
-40
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import React from 'react';
2+
import { WidgetProps } from '@rjsf/core';
3+
import InputSNCF from 'common/BootstrapSNCF/InputSNCF';
4+
5+
export const CustomPosition: React.FC<WidgetProps> = (props) => {
6+
const { schema, onChange, title, value } = props;
7+
const POINT_NAME = 'point-parameter';
8+
9+
return (
10+
<div key={`${POINT_NAME}-${schema.description}`}>
11+
<p>{title}</p>
12+
<InputSNCF
13+
name={POINT_NAME}
14+
id={`${POINT_NAME}-${schema.description}`}
15+
value={value}
16+
onChange={(e) => {
17+
onChange(e.target.value);
18+
}}
19+
type="number"
20+
min={0}
21+
/>
22+
</div>
23+
);
24+
};
25+
26+
export default CustomPosition;

front/src/applications/editor/tools/pointEdition/components.tsx

+18-6
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import {
2020
TrackSectionEntity,
2121
RouteEntity,
2222
SignalEntity,
23+
DetectorEntity,
24+
BufferStopEntity,
2325
} from 'types';
2426
import { osrdEditoastApi } from 'common/api/osrdEditoastApi';
2527
import EditorForm from 'applications/editor/components/EditorForm';
@@ -38,12 +40,16 @@ import { getEditRouteState } from 'applications/editor/tools/routeEdition/utils'
3840
import TOOL_TYPES from 'applications/editor/tools/toolTypes';
3941
import { EditoastType } from 'applications/editor/tools/types';
4042
import { getIsLoading } from 'reducers/main/mainSelector';
43+
import length from '@turf/length';
4144
import { CustomFlagSignalCheckbox } from './CustomFlagSignalCheckbox';
4245
import { PointEditionState } from './types';
4346
import { formatSignalingSystems } from './utils';
47+
import { CustomPosition } from './CustomPosition';
4448

4549
export const POINT_LAYER_ID = 'pointEditionTool/new-entity';
4650

51+
type EditorPoint = BufferStopEntity | DetectorEntity | SignalEntity;
52+
4753
/**
4854
* Generic component to show routes starting or ending from the edited waypoint:
4955
*/
@@ -218,7 +224,9 @@ export const PointEditionLeftPanel: FC<{ type: EditoastType }> = <Entity extends
218224

219225
if (!firstLoading) {
220226
const { position } = state.entity.properties;
221-
const point = along(track, position, { units: 'meters' });
227+
const turfPosition =
228+
(position * length(track, { units: 'meters' })) / track.properties.length;
229+
const point = along(track, turfPosition, { units: 'meters' });
222230

223231
setState({ ...state, entity: { ...state.entity, geometry: point.geometry } });
224232
}
@@ -261,6 +269,9 @@ export const PointEditionLeftPanel: FC<{ type: EditoastType }> = <Entity extends
261269
},
262270
},
263271
},
272+
position: {
273+
'ui:widget': CustomPosition,
274+
},
264275
}}
265276
onSubmit={async (savedEntity) => {
266277
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -297,8 +308,8 @@ export const PointEditionLeftPanel: FC<{ type: EditoastType }> = <Entity extends
297308
});
298309
}
299310
}}
300-
onChange={(entity: Entity | SignalEntity) => {
301-
const additionalUpdate: Partial<Entity> = {};
311+
onChange={(entity: Entity | EditorPoint) => {
312+
const additionalUpdate: Partial<EditorPoint> = {};
302313
const additionalPropertiesUpdate: Partial<SignalEntity['properties']> = {};
303314
const newPosition = entity.properties?.position;
304315
const oldPosition = state.entity.properties?.position;
@@ -311,16 +322,17 @@ export const PointEditionLeftPanel: FC<{ type: EditoastType }> = <Entity extends
311322
typeof oldPosition === 'number' &&
312323
newPosition !== oldPosition
313324
) {
314-
const point = along(trackState.track, newPosition, { units: 'meters' });
325+
const turfPosition =
326+
(newPosition * length(trackState.track, { units: 'meters' })) /
327+
trackState.track.properties.length;
328+
const point = along(trackState.track, turfPosition, { units: 'meters' });
315329
additionalUpdate.geometry = point.geometry;
316330
}
317-
318331
if (entity.objType === 'Signal' && entity.properties.logical_signals) {
319332
additionalPropertiesUpdate.logical_signals = formatSignalingSystems(
320333
entity as SignalEntity
321334
);
322335
}
323-
324336
setState({
325337
...state,
326338
entity: {

front/src/applications/editor/tools/pointEdition/tool-factory.tsx

+40-29
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,21 @@ import { Map } from 'maplibre-gl';
1111
import { save } from 'reducers/editor';
1212
import { ConfirmModal } from 'common/BootstrapSNCF/ModalSNCF';
1313
import { NEW_ENTITY_ID } from 'applications/editor/data/utils';
14+
import {
15+
NULL_GEOMETRY,
16+
BufferStopEntity,
17+
DetectorEntity,
18+
SignalEntity,
19+
TrackSectionEntity,
20+
} from 'types';
1421
import { LAYER_TO_EDITOAST_DICT, LayerType } from '../types';
1522
import { getNearestPoint } from '../../../../utils/mapHelper';
1623
import { getPointEditionLeftPanel, POINT_LAYER_ID, PointEditionMessages } from './components';
1724
import { PointEditionState } from './types';
18-
import { NULL_GEOMETRY, BufferStopEntity, DetectorEntity, SignalEntity } from '../../../../types';
1925
import { getEntity } from '../../data/api';
2026
import { Tool } from '../editorContextTypes';
2127
import { DEFAULT_COMMON_TOOL_STATE } from '../commonToolState';
28+
import { approximateDistanceWithEditoastData } from '../utils';
2229

2330
type EditorPoint = BufferStopEntity | DetectorEntity | SignalEntity;
2431
interface PointEditionToolParams<T extends EditorPoint> {
@@ -29,6 +36,14 @@ interface PointEditionToolParams<T extends EditorPoint> {
2936
requiresAngle?: boolean;
3037
}
3138

39+
function calculateDistanceAlongTrack(track: Feature<LineString>, point: Point) {
40+
const wrongPointOnTrack = nearestPointOnLine(track.geometry, point, { units: 'meters' });
41+
return approximateDistanceWithEditoastData(
42+
track as TrackSectionEntity,
43+
wrongPointOnTrack.geometry
44+
);
45+
}
46+
3247
function getPointEditionTool<T extends EditorPoint>({
3348
layer,
3449
icon,
@@ -61,20 +76,6 @@ function getPointEditionTool<T extends EditorPoint>({
6176
getInitialState,
6277
actions: [
6378
[
64-
{
65-
id: 'reset-entity',
66-
icon: BiReset,
67-
labelTranslationKey: `Editor.tools.${id}-edition.actions.reset-entity`,
68-
onClick({ setState, state }) {
69-
setState({
70-
...getInitialState(),
71-
entity: state.initialEntity,
72-
});
73-
},
74-
isDisabled({ state }) {
75-
return isEqual(state.entity, state.initialEntity);
76-
},
77-
},
7879
{
7980
id: 'new-entity',
8081
icon: AiOutlinePlus,
@@ -83,6 +84,19 @@ function getPointEditionTool<T extends EditorPoint>({
8384
setState(getInitialState());
8485
},
8586
},
87+
{
88+
id: 'reset-entity',
89+
icon: BiReset,
90+
labelTranslationKey: `Editor.tools.${id}-edition.actions.reset-entity`,
91+
isDisabled({ state: { entity, initialEntity } }) {
92+
return isEqual(entity, initialEntity);
93+
},
94+
onClick({ setState, state: { initialEntity } }) {
95+
setState({
96+
entity: cloneDeep(initialEntity),
97+
});
98+
},
99+
},
86100
],
87101
[
88102
{
@@ -116,6 +130,12 @@ function getPointEditionTool<T extends EditorPoint>({
116130
],
117131

118132
// Interactions:
133+
getCursor({ state }, { isDragging }) {
134+
if (isDragging || !state.entity.geometry || isEqual(state.entity.geometry, NULL_GEOMETRY))
135+
return 'move';
136+
if (state.isHoveringTarget) return 'pointer';
137+
return 'default';
138+
},
119139
onClickMap(_e, { setState, state, infraID, dispatch }) {
120140
const { isHoveringTarget, entity, nearestPoint } = state;
121141
if (entity.geometry && !isEqual(entity.geometry, NULL_GEOMETRY) && isHoveringTarget) {
@@ -125,7 +145,6 @@ function getPointEditionTool<T extends EditorPoint>({
125145
entity: omit(entity, 'geometry') as T,
126146
});
127147
}
128-
129148
if ((!entity.geometry || isEqual(entity.geometry, NULL_GEOMETRY)) && nearestPoint) {
130149
const newEntity = cloneDeep(entity);
131150
newEntity.geometry = {
@@ -135,15 +154,13 @@ function getPointEditionTool<T extends EditorPoint>({
135154
newEntity.properties = newEntity.properties || {};
136155
newEntity.properties.track = nearestPoint.trackSectionID;
137156

138-
// retrieve the track section to be sure that the computation of the distance will be good
139-
// we can't trust maplibre, because the stored gemetry is not necessary the real one
140157
getEntity(infraID as number, newEntity.properties.track, 'TrackSection', dispatch).then(
141158
(track) => {
142-
newEntity.properties.position = nearestPointOnLine(
143-
(track as Feature<LineString>).geometry,
144-
newEntity.geometry as Point,
145-
{ units: 'meters' }
146-
).properties?.location;
159+
const distanceAlongTrack = calculateDistanceAlongTrack(
160+
track as TrackSectionEntity,
161+
newEntity.geometry as Point
162+
);
163+
newEntity.properties.position = distanceAlongTrack;
147164

148165
setState({
149166
...state,
@@ -244,12 +261,6 @@ function getPointEditionTool<T extends EditorPoint>({
244261
getInteractiveLayers() {
245262
return ['editor/geo/track-main', POINT_LAYER_ID];
246263
},
247-
getCursor({ state }, { isDragging }) {
248-
if (isDragging || !state.entity.geometry || isEqual(state.entity.geometry, NULL_GEOMETRY))
249-
return 'move';
250-
if (state.isHoveringTarget) return 'pointer';
251-
return 'default';
252-
},
253264

254265
layersComponent,
255266
leftPanelComponent: getPointEditionLeftPanel(LAYER_TO_EDITOAST_DICT[layer]),

front/src/applications/editor/tools/utils.ts

+11-5
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,19 @@ import { getEntity } from '../data/api';
3030
* Since Turf and Editoast do not compute the lengths the same way (see #1751)
3131
* we can have data "end" being larger than Turf's computed length, which
3232
* throws an error. Until we find a way to get similar computations, we can
33-
* approximate this way:
33+
* approximate it with a rule of Three.
34+
*
35+
* This approximation is not good if the track is long.
3436
*/
3537
export function approximateDistanceWithEditoastData(track: TrackSectionEntity, point: Point) {
36-
const distanceAlongTrack =
37-
(length(lineSlice(track.geometry.coordinates[0], point, track)) * track.properties.length) /
38-
length(track);
39-
return distanceAlongTrack;
38+
const wrongDistanceAlongTrack = length(lineSlice(track.geometry.coordinates[0], point, track));
39+
const wrongTrackLength = length(track);
40+
const realTrackLength = track.properties.length;
41+
42+
const distanceAlongTrack = (wrongDistanceAlongTrack * realTrackLength) / wrongTrackLength;
43+
44+
if (Math.abs(distanceAlongTrack - realTrackLength) < 0.1) return realTrackLength;
45+
return Math.round(distanceAlongTrack * 100) / 100;
4046
}
4147

4248
/** return the trackRanges near the mouse thanks to the hover event */

0 commit comments

Comments
 (0)