11package fr.sncf.osrd.path.implementations
22
3- import fr.sncf.osrd.geom.LineString
43import fr.sncf.osrd.path.interfaces.*
54import fr.sncf.osrd.sim_infra.api.*
6- import fr.sncf.osrd.sim_infra.impl.TemporarySpeedLimitManager
7- import fr.sncf.osrd.utils.Direction
8- import fr.sncf.osrd.utils.DistanceRangeMap
9- import fr.sncf.osrd.utils.distanceRangeMapOf
105import fr.sncf.osrd.utils.indexing.mutableDirStaticIdxArrayListOf
11- import fr.sncf.osrd.utils.mergeDistanceRangeMaps
126import fr.sncf.osrd.utils.units.Distance
137import fr.sncf.osrd.utils.units.Offset
148import fr.sncf.osrd.utils.units.meters
159
1610/* *
1711 * A ChunkPath is a list of directional track chunks which form a path, with beginOffset being the
1812 * offset on the first chunk, and endOffset on the last chunk. *
13+ *
14+ * TODO path migration: remove remaining uses
1915 */
2016data class ChunkPath (
2117 /* *
@@ -40,305 +36,6 @@ data class ChunkPath(
4036 val length: Distance = endOffset.distance - beginOffset.distance
4137}
4238
43- data class PathPropertiesImpl (
44- val infra : RawSignalingInfra ,
45- val chunkPath : ChunkPath ,
46- val pathRoutes : List <RouteId >? ,
47- ) : PathProperties {
48- override fun getSlopes (): DistanceRangeMap <Double > {
49- return getRangeMap { dirChunkId -> infra.getTrackChunkSlope(dirChunkId) }
50- }
51-
52- override fun getOperationalPointParts ():
53- List <IdxWithTravelledPathOffset <OperationalPointPart >> {
54- return getElementsOnPath { dirChunkId ->
55- infra.getTrackChunkOperationalPointParts(dirChunkId.value).map { opId ->
56- IdxWithOffset (opId, infra.getOperationalPointPartChunkOffset(opId))
57- }
58- }
59- }
60-
61- override fun getGradients (): DistanceRangeMap <Double > {
62- return getRangeMap { dirChunkId -> infra.getTrackChunkGradient(dirChunkId) }
63- }
64-
65- override fun getCurves (): DistanceRangeMap <Double > {
66- return getRangeMap { dirChunkId -> infra.getTrackChunkCurve(dirChunkId) }
67- }
68-
69- override fun getGeo (): LineString {
70- return projectLineString { chunkId -> infra.getTrackChunkGeom(chunkId) }
71- }
72-
73- override fun getLoadingGauge (): DistanceRangeMap <LoadingGaugeConstraint > {
74- return getRangeMapFromUndirected { chunkId ->
75- infra.getTrackChunkLoadingGaugeConstraints(chunkId)
76- }
77- }
78-
79- override fun getElectrification (): DistanceRangeMap <Set <String >> {
80- return getRangeMapFromUndirected { chunkId ->
81- infra.getTrackChunkElectrificationVoltage(chunkId)
82- }
83- }
84-
85- override fun getNeutralSections (): DistanceRangeMap <NeutralSection > {
86- return getRangeMap { dirChunkId -> infra.getTrackChunkNeutralSections(dirChunkId) }
87- }
88-
89- override fun getSpeedLimitProperties (
90- trainTag : String? ,
91- temporarySpeedLimitManager : TemporarySpeedLimitManager ? ,
92- ): DistanceRangeMap <SpeedLimitProperty > {
93- assert (pathRoutes != null ) {
94- " the routes on a path should be set when attempting to compute a speed limit"
95- }
96- return getRangeMap { dirChunkId ->
97- val routeOnChunk =
98- infra.getRoutesOnTrackChunk(dirChunkId).filter { route ->
99- pathRoutes!! .contains(route)
100- }
101- // TODO: add a warning.
102- // Technically, in the following situation, the path would loop, and you could have 2
103- // itineraries in the path with the same commonChunk, with no way to know the true speed
104- // limit. For now, we take the first itinerary's speed limit.
105- // -> - -
106- // \
107- // end
108- // \
109- // - start - - - commonChunk - ->
110- val route = routeOnChunk.firstOrNull()?.let { routeId -> infra.getRouteName(routeId) }
111- val permanentSpeedLimits =
112- infra.getTrackChunkSpeedLimitProperties(dirChunkId, trainTag, route)
113- if (temporarySpeedLimitManager != null ) {
114- temporarySpeedLimitManager.speedLimits[dirChunkId]?.let { applicableSpeedLimits ->
115- permanentSpeedLimits.updateMap(
116- applicableSpeedLimits,
117- { s1, s2 ->
118- if (s1.speed < s2.speed) {
119- s1
120- } else {
121- s2
122- }
123- },
124- )
125- }
126- }
127- permanentSpeedLimits
128- }
129- }
130-
131- override fun getZones (): DistanceRangeMap <ZoneId > {
132- return getRangeMapFromUndirected { chunkId ->
133- val zoneId = infra.getTrackChunkZone(chunkId)
134- if (zoneId != null ) {
135- val chunkLength = infra.getTrackChunkLength(chunkId).distance
136- distanceRangeMapOf(
137- DistanceRangeMap .RangeMapEntry (Distance .ZERO , chunkLength, zoneId)
138- )
139- } else {
140- distanceRangeMapOf()
141- }
142- }
143- }
144-
145- override fun getLength (): Distance {
146- return chunkPath.endOffset - chunkPath.beginOffset
147- }
148-
149- override fun getTrackLocationAtOffset (pathOffset : Offset <TravelledPath >): TrackLocation {
150- val offset: Offset <TravelledPath > = pathOffset + chunkPath.beginOffset.distance
151- var lengthPrevChunks = 0 .meters
152- for (chunk in chunkPath.chunks) {
153- val chunkLength = infra.getTrackChunkLength(chunk.value)
154- if (lengthPrevChunks + chunkLength.distance >= offset.distance) {
155- val trackId = infra.getTrackFromChunk(chunk.value)
156- val startChunkOffset: Offset <TrackSection > = infra.getTrackChunkOffset(chunk.value)
157- val offsetOnChunk: Offset <TrackChunk > = Offset ((offset - lengthPrevChunks).distance)
158- return if (chunk.direction == Direction .INCREASING )
159- TrackLocation (trackId, startChunkOffset + offsetOnChunk.distance)
160- else
161- TrackLocation (
162- trackId,
163- startChunkOffset + chunkLength.distance - offsetOnChunk.distance,
164- )
165- }
166- lengthPrevChunks + = chunkLength.distance
167- }
168- throw RuntimeException (" The given path offset is larger than the path length" )
169- }
170-
171- override fun getTrackLocationOffset (location : TrackLocation ): Offset <TravelledPath >? {
172- val offset =
173- getOffsetOfTrackLocationOnChunks(infra, location, chunkPath.chunks) ? : return null
174- if (offset < chunkPath.beginOffset || offset > chunkPath.endOffset) return null
175- return Offset (offset - chunkPath.beginOffset)
176- }
177-
178- private fun projectLineString (getData : (chunkId: TrackChunkId ) -> LineString ): LineString {
179- fun getDirData (dirChunkId : DirTrackChunkId ): LineString {
180- val data = getData(dirChunkId.value)
181- if (dirChunkId.direction == Direction .INCREASING ) return data
182- else return data.reverse()!!
183- }
184-
185- fun sliceChunkData (
186- dirChunkId : DirTrackChunkId ,
187- beginChunkOffset : Offset <TrackChunk >? ,
188- endChunkOffset : Offset <TrackChunk >? ,
189- ): LineString {
190- val chunkLength = infra.getTrackChunkLength(dirChunkId.value).meters
191- val beginSliceOffset = beginChunkOffset?.distance?.meters ? : 0.0
192- val endSliceOffset = endChunkOffset?.distance?.meters ? : chunkLength
193- return getDirData(dirChunkId)
194- .slice(beginSliceOffset / chunkLength, endSliceOffset / chunkLength)
195- }
196-
197- val chunks = chunkPath.chunks
198- if (chunks.size == 0 ) return LineString .make(doubleArrayOf(), doubleArrayOf())
199- if (chunks.size == 1 )
200- return sliceChunkData(
201- chunks.first(),
202- chunkPath.beginOffset.cast(),
203- chunkPath.endOffset.cast(),
204- )
205-
206- val lineStrings = arrayListOf<LineString >()
207- lineStrings.add(sliceChunkData(chunks.first(), chunkPath.beginOffset.cast(), null ))
208- var totalChunkDistance = infra.getTrackChunkLength(chunks.first().value).distance
209- for (i in 1 until chunks.size - 1 ) {
210- lineStrings.add(getDirData(chunks[i]))
211- totalChunkDistance + = infra.getTrackChunkLength(chunks[i].value).distance
212- }
213- lineStrings.add(
214- sliceChunkData(chunks.last(), null , (chunkPath.endOffset - totalChunkDistance).cast())
215- )
216- return LineString .concatenate(lineStrings)
217- }
218-
219- /* *
220- * Use the given function to get the range data from a chunk, and concatenates all the values on
221- * the path
222- */
223- private fun <T > getRangeMap (
224- getData : (dirChunkId: DirTrackChunkId ) -> DistanceRangeMap <T >
225- ): DistanceRangeMap <T > {
226- val maps = ArrayList <DistanceRangeMap <T >>()
227- val distances = ArrayList <Distance >()
228- for (dirChunk in chunkPath.chunks) {
229- maps.add(getData.invoke(dirChunk))
230- distances.add(infra.getTrackChunkLength(dirChunk.value).distance)
231- }
232- distances.removeLast()
233- val mergedMap = mergeDistanceRangeMaps(maps, distances)
234- mergedMap.truncate(chunkPath.beginOffset.distance, chunkPath.endOffset.distance)
235- mergedMap.shiftPositions(- chunkPath.beginOffset.distance)
236- return mergedMap
237- }
238-
239- /* *
240- * Use the given function to get the range data from a chunk, and concatenates all the values on
241- * the path. This version is for undirected data, such as voltage or loading gauge constraints
242- */
243- override fun <T > getRangeMapFromUndirected (
244- getData : (chunkId: TrackChunkId ) -> DistanceRangeMap <T >
245- ): DistanceRangeMap <T > {
246- fun projectDirection (dirChunk : DirTrackChunkId ): DistanceRangeMap <T > {
247- val data = getData(dirChunk.value)
248- if (dirChunk.direction == Direction .INCREASING ) return data
249- val chunkLength = infra.getTrackChunkLength(dirChunk.value).distance
250- val res = distanceRangeMapOf<T >()
251- for (entry in data) {
252- assert (0 .meters <= entry.lower && entry.lower <= chunkLength)
253- assert (0 .meters <= entry.upper && entry.upper <= chunkLength)
254- res.put(chunkLength - entry.upper, chunkLength - entry.lower, entry.value)
255- }
256- return res
257- }
258- return getRangeMap { dirChunkId -> projectDirection(dirChunkId) }
259- }
260-
261- override fun withRoutes (routes : List <RouteId >): PathProperties {
262- return copy(pathRoutes = routes)
263- }
264-
265- /* *
266- * Use the given function to get punctual data from a chunk, and concatenates all the values on
267- * the path. // TODO change type: types should be DirStaticIdxList<TrackChunk>
268- */
269- private fun <T > getElementsOnPath (
270- getData : (chunk: DirTrackChunkId ) -> Iterable <IdxWithOffset <T , TrackChunk >>
271- ): List <IdxWithTravelledPathOffset <T >> {
272- val res = ArrayList <IdxWithBlockPathOffset <T >>()
273- var chunkOffset = Offset <BlockPath >(0 .meters)
274- for (chunk in chunkPath.chunks) {
275- for ((element, offset) in getData.invoke(chunk)) {
276- val projectedOffset = projectPosition(chunk, offset)
277- res.add(IdxWithOffset (element, chunkOffset + projectedOffset.distance))
278- }
279- chunkOffset + = infra.getTrackChunkLength(chunk.value).distance
280- }
281- val filtered = filterAndShiftElementsOnPath(res)
282- return filtered.sortedBy { x -> x.offset }
283- }
284-
285- /* *
286- * Given a directional chunk and a position on said chunk, projects the position according to
287- * the direction
288- */
289- private fun projectPosition (
290- dirChunkId : DirTrackChunkId ,
291- position : Offset <TrackChunk >,
292- ): Offset <TrackChunk > {
293- val chunkId = dirChunkId.value
294- val end = infra.getTrackChunkLength(chunkId)
295- if (dirChunkId.direction == Direction .INCREASING ) return position
296- else return Offset (end - position)
297- }
298-
299- /* *
300- * Keeps only the elements that are not outside the path, and shift the offsets to start at 0 //
301- * TODO change type: type DirStaticIdxList<TrackChunk> (= Offset<ChunkSequence> in and
302- * Offset<TravelledPath> out)
303- */
304- private fun <T > filterAndShiftElementsOnPath (
305- res : List <IdxWithBlockPathOffset <T >>
306- ): List <IdxWithTravelledPathOffset <T >> {
307- return res.filter { element ->
308- element.offset >= chunkPath.beginOffset && element.offset <= chunkPath.endOffset
309- }
310- .map { element ->
311- IdxWithOffset (element.value, Offset (element.offset - chunkPath.beginOffset))
312- }
313- }
314- }
315-
316- /* * Returns the offset of a location on a given list of chunks */
317- fun getOffsetOfTrackLocationOnChunks (
318- infra : TrackProperties ,
319- location : TrackLocation ,
320- chunks : List <DirTrackChunkId >,
321- ): Offset <BlockPath >? {
322- var offsetAfterFirstChunk = Offset <BlockPath >(0 .meters)
323- for (dirChunk in chunks) {
324- val chunkLength = infra.getTrackChunkLength(dirChunk.value)
325- if (location.trackId == infra.getTrackFromChunk(dirChunk.value)) {
326- val chunkOffset = infra.getTrackChunkOffset(dirChunk.value)
327- if (
328- chunkOffset <= location.offset &&
329- location.offset <= (chunkOffset + chunkLength.distance)
330- ) {
331- val distanceToChunkStart =
332- if (dirChunk.direction == Direction .INCREASING ) location.offset - chunkOffset
333- else (chunkOffset + chunkLength.distance) - location.offset
334- return offsetAfterFirstChunk + distanceToChunkStart
335- }
336- }
337- offsetAfterFirstChunk + = chunkLength.distance
338- }
339- return null
340- }
341-
34239/* *
34340 * Build chunkPath, which is the subset of the given chunks corresponding to the beginOffset and
34441 * endOffset. *
0 commit comments