Skip to content

Commit e6d34e6

Browse files
committed
core: paths: add tooling for zone ranges
Signed-off-by: Eloi Charpentier <[email protected]>
1 parent 3965f01 commit e6d34e6

File tree

6 files changed

+161
-5
lines changed

6 files changed

+161
-5
lines changed

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ data class TrainPathNoBacktrack(
3434

3535
private val cachedEnvelopeSimPath by lazy { computeEnvelopeSimPath() }
3636

37+
private val cachedZonePaths by lazy {
38+
assert(routes!!.isNotEmpty())
39+
mapSubObjects(routes, rawInfra::getRoutePath, rawInfra::getZonePathLength)
40+
}
41+
3742
init {
3843
// The sanity checks here are quite exhaustive and might be expensive to compute.
3944
// Once the path types are stable, we can remove some of the tests.
@@ -86,6 +91,8 @@ data class TrainPathNoBacktrack(
8691

8792
override fun getChunks(): List<DirChunkRange> = chunks
8893

94+
override fun getZonePaths(): List<ZonePathRange> = cachedZonePaths
95+
8996
override val length: Double
9097
get() = pathProperties.getLength().meters
9198

core/kt-osrd-path/src/main/kotlin/fr/sncf/osrd/path/interfaces/GenericLinearRange.kt

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@ package fr.sncf.osrd.path.interfaces
33
import fr.sncf.osrd.sim_infra.api.Block
44
import fr.sncf.osrd.sim_infra.api.Route
55
import fr.sncf.osrd.sim_infra.api.TrackChunk
6+
import fr.sncf.osrd.sim_infra.api.Zone
7+
import fr.sncf.osrd.sim_infra.api.ZonePath
68
import fr.sncf.osrd.utils.indexing.DirStaticIdx
79
import fr.sncf.osrd.utils.indexing.StaticIdx
10+
import fr.sncf.osrd.utils.units.Length
811
import fr.sncf.osrd.utils.units.Offset
912
import fr.sncf.osrd.utils.units.Offset.Companion.max
1013
import fr.sncf.osrd.utils.units.Offset.Companion.min
@@ -27,13 +30,13 @@ data class GenericLinearRange<ValueType, OffsetType>(
2730
fun isSingleton() = length == 0.meters
2831

2932
fun offsetFromTrainPath(pathOffset: Offset<TrainPath>): Offset<OffsetType> {
30-
val rangeOffset = pathOffset.distance - pathBegin.distance
31-
return objectBegin + rangeOffset
33+
val objectStart = Offset<TrainPath>(pathBegin.distance - objectBegin.distance)
34+
return Offset(pathOffset.distance - objectStart.distance)
3235
}
3336

3437
fun offsetToTrainPath(objectOffset: Offset<OffsetType>): Offset<TrainPath> {
35-
val rangeOffset = objectOffset.distance - objectBegin.distance
36-
return pathBegin + rangeOffset
38+
val objectStart = Offset<TrainPath>(pathBegin.distance - objectBegin.distance)
39+
return objectStart + objectOffset.distance
3740
}
3841

3942
fun withTruncatedPathRange(
@@ -78,6 +81,18 @@ data class GenericLinearRange<ValueType, OffsetType>(
7881
}
7982
return res
8083
}
84+
85+
fun <T, NewOffsetType> mapValue(value: T): GenericLinearRange<T, NewOffsetType> {
86+
return GenericLinearRange(value, objectBegin.cast(), objectEnd.cast(), pathBegin, pathEnd)
87+
}
88+
89+
// Where the object begins on the path, not just the range. May be negative.
90+
fun getObjectAbsolutePathStart() = pathBegin - objectBegin.distance
91+
92+
// Where the object ends on the path, not just the range. May be larger than path length.
93+
fun getObjectAbsolutePathEnd(objectLength: Length<OffsetType>): Offset<TrainPath> {
94+
return getObjectAbsolutePathStart() + objectLength.distance
95+
}
8196
}
8297

8398
typealias LinearObjectRange<T> = GenericLinearRange<StaticIdx<T>, T>
@@ -88,6 +103,10 @@ typealias RouteRange = LinearObjectRange<Route>
88103

89104
typealias BlockRange = LinearObjectRange<Block>
90105

106+
typealias ZoneRange = LinearObjectRange<Zone>
107+
108+
typealias ZonePathRange = LinearObjectRange<ZonePath>
109+
91110
typealias DirChunkRange = LinearDirObjectRange<TrackChunk>
92111

93112
fun <ValueType, OffsetType> mergeLinearRanges(

core/kt-osrd-path/src/main/kotlin/fr/sncf/osrd/path/interfaces/PathProperties.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ sealed interface BlockPath
2828
* A marker type for Length and Offset. In TravelledPath, start refers to the real start of the head
2929
* of the train.
3030
*/
31-
sealed interface TravelledPath
31+
// TODO path migration: remove TravelledPath entirely
32+
typealias TravelledPath = TrainPath
3233

3334
@Suppress("INAPPLICABLE_JVM_NAME")
3435
interface PathProperties {

core/kt-osrd-path/src/main/kotlin/fr/sncf/osrd/path/interfaces/TrainPath.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,19 @@ interface TrainPath : PhysicsPath, PathProperties {
4848
fun getRoutes(): List<RouteRange>
4949

5050
fun getChunks(): List<DirChunkRange>
51+
52+
fun getZonePaths(): List<ZonePathRange>
5153
// To be expanded as needed with other linear objects
5254
}
5355

5456
fun concat(vararg paths: TrainPath): TrainPath {
5557
TODO("Required for actual backtracks, not necessary earlier than that")
5658
}
5759

60+
fun TrainPath.getZones(rawInfra: RawInfra): List<ZoneRange> {
61+
return getZonePaths().map { it.mapValue(rawInfra.getZonePathZone(it.value)) }
62+
}
63+
5864
// Extension functions that help with backward compatibility.
5965
// These should only exist during the migration to enable more local changes,
6066
// to allow partial migration while still having a working core.
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package fr.sncf.osrd.path
2+
3+
import fr.sncf.osrd.path.interfaces.GenericLinearRange
4+
import fr.sncf.osrd.path.interfaces.mapSubObjects
5+
import fr.sncf.osrd.sim_infra.api.Block
6+
import fr.sncf.osrd.sim_infra.api.BlockId
7+
import fr.sncf.osrd.sim_infra.api.Route
8+
import fr.sncf.osrd.sim_infra.api.RouteId
9+
import fr.sncf.osrd.utils.units.Length
10+
import fr.sncf.osrd.utils.units.Offset
11+
import fr.sncf.osrd.utils.units.meters
12+
import kotlin.test.assertEquals
13+
import org.junit.Test
14+
15+
class GenericLinearRangeTest {
16+
@Test
17+
fun testProjectionFullRange() {
18+
/*
19+
block offset (m) 0 50 100
20+
block: |---------------->
21+
projected offset: | x
22+
path: --------|---------------->
23+
path offset (m) 1_000 1_050 1_100
24+
*/
25+
val range =
26+
GenericLinearRange(
27+
value = BlockId(0U),
28+
objectBegin = Offset<Block>(0.meters),
29+
objectEnd = Offset(100.meters),
30+
pathBegin = Offset(1_000.meters),
31+
pathEnd = Offset(1_100.meters),
32+
)
33+
34+
assertEquals(Offset(50.meters), range.offsetFromTrainPath(Offset(1_050.meters)))
35+
assertEquals(Offset(1_050.meters), range.offsetToTrainPath(Offset(50.meters)))
36+
assertEquals(Offset(1_000.meters), range.getObjectAbsolutePathStart())
37+
assertEquals(Offset(1_100.meters), range.getObjectAbsolutePathEnd(Length(100.meters)))
38+
}
39+
40+
@Test
41+
fun testProjectionPartialRange() {
42+
/*
43+
block offset (m) 0 25 42 75 100
44+
block: |----|------------|------->
45+
x
46+
path: |------------>
47+
path offset (m) 0 17 50
48+
*/
49+
val range =
50+
GenericLinearRange(
51+
value = BlockId(0U),
52+
objectBegin = Offset<Block>(25.meters),
53+
objectEnd = Offset(75.meters),
54+
pathBegin = Offset(0.meters),
55+
pathEnd = Offset(50.meters),
56+
)
57+
58+
assertEquals(Offset(42.meters), range.offsetFromTrainPath(Offset(17.meters)))
59+
assertEquals(Offset(17.meters), range.offsetToTrainPath(Offset(42.meters)))
60+
assertEquals(Offset((-25).meters), range.getObjectAbsolutePathStart())
61+
assertEquals(Offset(75.meters), range.getObjectAbsolutePathEnd(Length(100.meters)))
62+
}
63+
64+
@Test
65+
fun testMapSubRange() {
66+
/*
67+
route offset (m) 0 100 250 400 600 750 800 1000
68+
route: |---+---|----+--------+-----|---+------>
69+
path: | + |----+--------+-----> + +
70+
path offset (m) -250 + 0 150 350 500 550 750
71+
blocks: |--->|--+---->|------->|----+--->|----->
72+
block offsets 0 100 ^ 300 200 150 200 200
73+
block offsets 150
74+
*/
75+
val range =
76+
GenericLinearRange(
77+
value = RouteId(0U),
78+
objectBegin = Offset<Route>(250.meters),
79+
objectEnd = Offset(750.meters),
80+
pathBegin = Offset(0.meters),
81+
pathEnd = Offset(500.meters),
82+
)
83+
val blocks = (0U..4U).map { BlockId(it) }
84+
val blockLengths = listOf(100, 300, 200, 200, 200).map { Length<Block>(it.meters) }
85+
86+
val blockRanges =
87+
mapSubObjects(
88+
listOf(range),
89+
listSubObject = { blocks },
90+
subObjectLength = { blockLengths[it.index.toInt()] },
91+
)
92+
93+
val expectedRanges =
94+
listOf(
95+
GenericLinearRange(
96+
value = BlockId(1U),
97+
objectBegin = Offset<Block>(150.meters),
98+
objectEnd = Offset(300.meters),
99+
pathBegin = Offset(0.meters),
100+
pathEnd = Offset(150.meters),
101+
),
102+
GenericLinearRange(
103+
value = BlockId(2U),
104+
objectBegin = Offset(0.meters),
105+
objectEnd = Offset(200.meters),
106+
pathBegin = Offset(150.meters),
107+
pathEnd = Offset(350.meters),
108+
),
109+
GenericLinearRange(
110+
value = BlockId(3U),
111+
objectBegin = Offset(0.meters),
112+
objectEnd = Offset(150.meters),
113+
pathBegin = Offset(350.meters),
114+
pathEnd = Offset(500.meters),
115+
),
116+
)
117+
assertEquals(expectedRanges, blockRanges)
118+
}
119+
}

core/src/test/kotlin/fr/sncf/osrd/utils/PathUtils.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ fun pathFromTracks(
2424
start: Distance,
2525
end: Distance,
2626
electricalProfileMapping: ElectricalProfileMapping? = null,
27+
routeNames: List<String>? = null,
2728
): TrainPath {
2829
val chunkList = mutableDirStaticIdxArrayListOf<TrackChunk>()
2930
trackIds
@@ -36,6 +37,7 @@ fun pathFromTracks(
3637
blockInfra,
3738
chunkPath,
3839
electricalProfileMapping = electricalProfileMapping,
40+
routeNames = routeNames,
3941
)
4042
}
4143

@@ -46,6 +48,7 @@ fun pathFromTracks(
4648
start: Distance,
4749
end: Distance,
4850
electricalProfileMapping: ElectricalProfileMapping? = null,
51+
routeNames: List<String>? = null,
4952
): TrainPath {
5053
return pathFromTracks(
5154
infra.rawInfra,
@@ -55,6 +58,7 @@ fun pathFromTracks(
5558
start,
5659
end,
5760
electricalProfileMapping = electricalProfileMapping,
61+
routeNames = routeNames,
5862
)
5963
}
6064

0 commit comments

Comments
 (0)