Skip to content

Commit ae4a975

Browse files
committed
core: integrate path properties into TrainPath implementation
Signed-off-by: Eloi Charpentier <[email protected]>
1 parent 7592d74 commit ae4a975

File tree

10 files changed

+246
-551
lines changed

10 files changed

+246
-551
lines changed

core/kt-osrd-path/src/main/kotlin/fr/sncf/osrd/path/implementations/PathPropertiesImpl.kt

Lines changed: 2 additions & 305 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,17 @@
11
package fr.sncf.osrd.path.implementations
22

3-
import fr.sncf.osrd.geom.LineString
43
import fr.sncf.osrd.path.interfaces.*
54
import 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
105
import fr.sncf.osrd.utils.indexing.mutableDirStaticIdxArrayListOf
11-
import fr.sncf.osrd.utils.mergeDistanceRangeMaps
126
import fr.sncf.osrd.utils.units.Distance
137
import fr.sncf.osrd.utils.units.Offset
148
import 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
*/
2016
data 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

Comments
 (0)