Skip to content

Commit afb63cf

Browse files
added support for regular and explicit road marks
1 parent e94f6dd commit afb63cf

File tree

15 files changed

+476
-118
lines changed

15 files changed

+476
-118
lines changed

rtron-main/src/main/kotlin/io/rtron/main/processor/OpendriveToCitygmlProcessor.kt

+1
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ class OpendriveToCitygmlProcessor(
136136
OpendriveWriter.writeToFile(opendriveRemovedObjectResult.first, opendriveFilePath)
137137

138138
// transform OpenDRIVE model to Roadspaces model
139+
139140
val opendrive2RoadspacesTransformer = Opendrive2RoadspacesTransformer(parameters.deriveOpendrive2RoadspacesParameters())
140141
val roadspacesModelResult = opendrive2RoadspacesTransformer.transform(opendriveRemovedObjectResult.first)
141142
roadspacesModelResult.second.serializeToJsonFile(outputSubDirectoryPath / OPENDRIVE_TO_ROADSPACES_REPORT_PATH)

rtron-math/src/main/kotlin/io/rtron/math/analysis/function/univariate/pure/ConstantFunction.kt

+2
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ data class ConstantFunction(
3838
// Operators
3939
infix fun timesValue(other: Double): ConstantFunction = copy(value = this.value * other)
4040

41+
infix fun plusValue(other: Double): ConstantFunction = copy(value = this.value + other)
42+
4143
// Methods
4244
override fun valueUnbounded(x: Double): Either<IllegalArgumentException, Double> = Either.Right(value)
4345

rtron-model/src/main/kotlin/io/rtron/model/opendrive/additions/optics/Optics.kt

+15
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,17 @@ import io.rtron.model.opendrive.junction.connection
2929
import io.rtron.model.opendrive.lane.RoadLanes
3030
import io.rtron.model.opendrive.lane.RoadLanesLaneSection
3131
import io.rtron.model.opendrive.lane.RoadLanesLaneSectionCenter
32+
import io.rtron.model.opendrive.lane.RoadLanesLaneSectionCenterLane
3233
import io.rtron.model.opendrive.lane.RoadLanesLaneSectionLeft
34+
import io.rtron.model.opendrive.lane.RoadLanesLaneSectionLeftLane
3335
import io.rtron.model.opendrive.lane.RoadLanesLaneSectionRight
36+
import io.rtron.model.opendrive.lane.RoadLanesLaneSectionRightLane
3437
import io.rtron.model.opendrive.lane.center
3538
import io.rtron.model.opendrive.lane.lane
3639
import io.rtron.model.opendrive.lane.laneSection
3740
import io.rtron.model.opendrive.lane.left
3841
import io.rtron.model.opendrive.lane.right
42+
import io.rtron.model.opendrive.lane.roadMark
3943
import io.rtron.model.opendrive.objects.RoadObjects
4044
import io.rtron.model.opendrive.objects.RoadObjectsObject
4145
import io.rtron.model.opendrive.objects.RoadObjectsObjectOutlines
@@ -84,6 +88,17 @@ val everyRoadLanesLaneSectionCenterLane =
8488
everyLaneSection compose RoadLanesLaneSection.center compose
8589
RoadLanesLaneSectionCenter.lane
8690

91+
// road marks
92+
val everyRoadLanesLaneSectionLeftLaneRoadMark =
93+
everyRoadLanesLaneSectionLeftLane compose
94+
RoadLanesLaneSectionLeftLane.roadMark compose Traversal.list()
95+
val everyRoadLanesLaneSectionRightLaneRoadMark =
96+
everyRoadLanesLaneSectionRightLane compose
97+
RoadLanesLaneSectionRightLane.roadMark compose Traversal.list()
98+
val everyRoadLanesLaneSectionCenterLaneRoadMark =
99+
everyRoadLanesLaneSectionCenterLane compose
100+
RoadLanesLaneSectionCenterLane.roadMark compose Traversal.list()
101+
87102
// junction
88103
val everyJunction = OpendriveModel.junction compose Traversal.list()
89104
val everyJunctionConnection = everyJunction compose Junction.connection compose Traversal.list()

rtron-model/src/main/kotlin/io/rtron/model/opendrive/lane/RoadLanesLaneSectionLCRLaneRoadMark.kt

+14
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@
1616

1717
package io.rtron.model.opendrive.lane
1818

19+
import arrow.core.NonEmptyList
1920
import arrow.core.None
2021
import arrow.core.Option
22+
import arrow.core.flatten
23+
import arrow.core.toNonEmptyListOrNone
2124
import arrow.optics.optics
2225
import io.rtron.model.opendrive.additions.identifier.AdditionalLaneRoadMarkIdentifier
2326
import io.rtron.model.opendrive.additions.identifier.LaneRoadMarkIdentifier
@@ -38,5 +41,16 @@ data class RoadLanesLaneSectionLCRLaneRoadMark(
3841
var width: Option<Double> = None,
3942
override var additionalId: Option<LaneRoadMarkIdentifier> = None,
4043
) : OpendriveElement(), AdditionalLaneRoadMarkIdentifier {
44+
// Methods
45+
fun containsTypeLines() = type.isSome { it.line.isNotEmpty() }
46+
47+
fun containsExplicitLines() = explicit.isSome { it.line.isNotEmpty() }
48+
49+
fun getTypeLines(): Option<NonEmptyList<RoadLanesLaneSectionLCRLaneRoadMarkTypeLine>> =
50+
type.map { it.line.toNonEmptyListOrNone() }.flatten()
51+
52+
fun getExplicitLines(): Option<NonEmptyList<RoadLanesLaneSectionLCRLaneRoadMarkExplicitLine>> =
53+
explicit.map { it.line.toNonEmptyListOrNone() }.flatten()
54+
4155
companion object
4256
}

rtron-model/src/main/kotlin/io/rtron/model/opendrive/lane/RoadLanesLaneSectionLCRLaneRoadMarkExplicit.kt

+7-1
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,14 @@
1616

1717
package io.rtron.model.opendrive.lane
1818

19+
import arrow.core.NonEmptyList
20+
import arrow.core.Option
21+
import arrow.core.toNonEmptyListOrNone
1922
import io.rtron.model.opendrive.core.OpendriveElement
2023

2124
data class RoadLanesLaneSectionLCRLaneRoadMarkExplicit(
2225
var line: List<RoadLanesLaneSectionLCRLaneRoadMarkExplicitLine> = emptyList(),
23-
) : OpendriveElement()
26+
) : OpendriveElement() {
27+
// Methods
28+
fun getLineEntries(): Option<NonEmptyList<RoadLanesLaneSectionLCRLaneRoadMarkExplicitLine>> = line.toNonEmptyListOrNone()
29+
}

rtron-model/src/main/kotlin/io/rtron/model/opendrive/lane/RoadLanesLaneSectionLCRLaneRoadMarkType.kt

+7-1
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,16 @@
1616

1717
package io.rtron.model.opendrive.lane
1818

19+
import arrow.core.NonEmptyList
20+
import arrow.core.Option
21+
import arrow.core.toNonEmptyListOrNone
1922
import io.rtron.model.opendrive.core.OpendriveElement
2023

2124
data class RoadLanesLaneSectionLCRLaneRoadMarkType(
2225
var line: List<RoadLanesLaneSectionLCRLaneRoadMarkTypeLine> = emptyList(),
2326
var name: String = "",
2427
var width: Double = Double.NaN,
25-
) : OpendriveElement()
28+
) : OpendriveElement() {
29+
// Methods
30+
fun getLineEntries(): Option<NonEmptyList<RoadLanesLaneSectionLCRLaneRoadMarkTypeLine>> = line.toNonEmptyListOrNone()
31+
}

rtron-model/src/main/kotlin/io/rtron/model/roadspaces/roadspace/road/Road.kt

+17-16
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ import arrow.core.nonEmptyListOf
2727
import arrow.core.raise.either
2828
import arrow.core.right
2929
import arrow.core.toNonEmptyListOrNull
30-
import arrow.optics.copy
3130
import io.rtron.math.analysis.function.univariate.UnivariateFunction
3231
import io.rtron.math.analysis.function.univariate.combination.SectionedUnivariateFunction
3332
import io.rtron.math.analysis.function.univariate.combination.StackedFunction
@@ -646,19 +645,24 @@ class Road(
646645
roadMarking: RoadMarking,
647646
step: Double,
648647
): AbstractGeometry3D {
649-
if (roadMarking.width.value < geometricalTolerance) {
648+
require(roadMarking.domain.length >= geometricalTolerance) { "Domain must be above tolerance threshold." }
649+
if (roadMarking.width.isNone()) {
650650
return getCurveOnLaneSectionSurface(centerLane.id.laneSectionIdentifier, centerLane.level)
651651
.getOrElse { throw it }
652652
}
653653

654-
val leftOffsetFunction = roadMarking.width timesValue 0.5
654+
val leftOffsetFunction =
655+
roadMarking.getLeftOffsetFunction()
656+
.getOrElse { throw IllegalStateException("Case without width must have already been handled.") }
655657
val leftRoadMarkingBoundary =
656658
getCurveOnLaneSectionSurface(centerLane.id.laneSectionIdentifier, centerLane.level, leftOffsetFunction)
657659
.getOrElse { throw it }
658660
.calculatePointListGlobalCS(step)
659661
.mapLeft { it.toIllegalStateException() }
660662
.getOrElse { throw it }
661-
val rightOffsetFunction = roadMarking.width timesValue -0.5
663+
val rightOffsetFunction =
664+
roadMarking.getRightOffsetFunction()
665+
.getOrElse { throw IllegalStateException("Case without width must have already been handled.") }
662666
val rightRoadMarkingBoundary =
663667
getCurveOnLaneSectionSurface(centerLane.id.laneSectionIdentifier, centerLane.level, rightOffsetFunction)
664668
.getOrElse { throw it }
@@ -711,27 +715,24 @@ class Road(
711715
roadMarking: RoadMarking,
712716
step: Double,
713717
): Either<Exception, AbstractGeometry3D> {
714-
if (roadMarking.width.domain.length < geometricalTolerance) {
715-
return Either.Left(
716-
IllegalStateException(
717-
"${laneIdentifier.toIdentifierText()}: Road marking's length is zero (or below tolerance threshold) and " +
718-
"thus no surface can be constructed.",
719-
),
720-
)
721-
}
718+
require(roadMarking.domain.length >= geometricalTolerance) { "Domain must be above tolerance threshold." }
722719

723-
if (roadMarking.width.value < geometricalTolerance) {
724-
return getCurveOnLane(laneIdentifier, 1.0, roadMarking.width)
720+
if (roadMarking.width.isNone()) {
721+
return getCurveOnLane(laneIdentifier, 1.0, roadMarking.lateralOffsetFunction)
725722
}
726723

727-
val leftOffsetFunction = roadMarking.width timesValue 0.5
724+
val leftOffsetFunction =
725+
roadMarking.getLeftOffsetFunction()
726+
.getOrElse { throw IllegalStateException("Case without width must have already been handled.") }
728727
val leftRoadMarkBoundary =
729728
getCurveOnLane(laneIdentifier, 1.0, leftOffsetFunction)
730729
.getOrElse { throw it }
731730
.calculatePointListGlobalCS(step)
732731
.mapLeft { it.toIllegalStateException() }
733732
.getOrElse { throw it }
734-
val rightOffsetFunction = roadMarking.width timesValue -0.5
733+
val rightOffsetFunction =
734+
roadMarking.getRightOffsetFunction()
735+
.getOrElse { throw IllegalStateException("Case without width must have already been handled.") }
735736
val rightRoadMarkBoundary =
736737
getCurveOnLane(laneIdentifier, 1.0, rightOffsetFunction)
737738
.getOrElse { throw it }

rtron-model/src/main/kotlin/io/rtron/model/roadspaces/roadspace/road/RoadMarking.kt

+27-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@
1616

1717
package io.rtron.model.roadspaces.roadspace.road
1818

19+
import arrow.core.None
20+
import arrow.core.Option
21+
import arrow.core.getOrElse
22+
import arrow.core.some
1923
import io.rtron.math.analysis.function.univariate.pure.ConstantFunction
24+
import io.rtron.math.range.Range
2025
import io.rtron.model.roadspaces.roadspace.attribute.AttributeList
2126

2227
/**
@@ -26,12 +31,32 @@ import io.rtron.model.roadspaces.roadspace.attribute.AttributeList
2631
* @param attributes further information attributes
2732
*/
2833
data class RoadMarking(
29-
val width: ConstantFunction,
34+
val domain: Range<Double>,
35+
val width: Option<Double>,
36+
val lateralOffset: Option<Double>,
3037
val laneChange: LaneChange,
3138
val attributes: AttributeList,
3239
) {
3340
// Properties and Initializers
3441
init {
35-
require(width.domain.isNotEmpty()) { "The domain of the road marking's width must not be empty." }
42+
require(domain.isNotEmpty()) { "The domain of the road marking's width must not be empty." }
43+
}
44+
45+
val widthFunction get(): Option<ConstantFunction> = width.map { ConstantFunction(it, domain) }
46+
47+
val lateralOffsetFunctionOptional get(): Option<ConstantFunction> = lateralOffset.map { ConstantFunction(it, domain) }
48+
49+
val lateralOffsetFunction get(): ConstantFunction = ConstantFunction(lateralOffset.getOrElse { 0.0 }, domain)
50+
51+
// Methods
52+
53+
fun getLeftOffsetFunction(): Option<ConstantFunction> {
54+
val offsetValue = width.map { 0.5 * it }.getOrElse { return None } + lateralOffset.getOrElse { 0.0 }
55+
return ConstantFunction(offsetValue, domain).some()
56+
}
57+
58+
fun getRightOffsetFunction(): Option<ConstantFunction> {
59+
val offsetValue = width.map { -0.5 * it }.getOrElse { return None } + lateralOffset.getOrElse { 0.0 }
60+
return ConstantFunction(offsetValue, domain).some()
3661
}
3762
}

rtron-transformer/src/main/kotlin/io/rtron/transformer/converter/opendrive2roadspaces/Opendrive2RoadspacesTransformer.kt

+8-4
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import io.rtron.model.roadspaces.roadspace.Roadspace
3030
import io.rtron.transformer.converter.opendrive2roadspaces.header.HeaderBuilder
3131
import io.rtron.transformer.converter.opendrive2roadspaces.junction.JunctionBuilder
3232
import io.rtron.transformer.converter.opendrive2roadspaces.report.Opendrive2RoadspacesReport
33+
import io.rtron.transformer.converter.opendrive2roadspaces.roadspaces.RoadMarkRepresentationRegistry
3334
import io.rtron.transformer.converter.opendrive2roadspaces.roadspaces.RoadspaceBuilder
3435
import kotlinx.coroutines.DelicateCoroutinesApi
3536
import kotlinx.coroutines.GlobalScope
@@ -68,12 +69,13 @@ class Opendrive2RoadspacesTransformer(
6869
val header = headerBuilder.buildHeader(opendriveModel.header).handleIssueList { report.conversion += it }
6970

7071
// transformation of each road
72+
val roadMarkRepresentationRegistry = RoadMarkRepresentationRegistry.fromHighestOccurrence(opendriveModel)
7173
val progressBar = ProgressBar("Transforming roads", opendriveModel.road.size)
7274
val roadspacesWithContextReports =
7375
if (parameters.concurrentProcessing) {
74-
transformRoadspacesConcurrently(opendriveModel, progressBar)
76+
transformRoadspacesConcurrently(opendriveModel, roadMarkRepresentationRegistry, progressBar)
7577
} else {
76-
transformRoadspacesSequentially(opendriveModel, progressBar)
78+
transformRoadspacesSequentially(opendriveModel, roadMarkRepresentationRegistry, progressBar)
7779
}
7880

7981
val roadspaces = roadspacesWithContextReports.mergeIssueLists().handleIssueList { report.conversion += it }
@@ -91,21 +93,23 @@ class Opendrive2RoadspacesTransformer(
9193

9294
private fun transformRoadspacesSequentially(
9395
opendriveModel: OpendriveModel,
96+
roadMarkRepresentationRegistry: RoadMarkRepresentationRegistry,
9497
progressBar: ProgressBar,
9598
): List<ContextIssueList<Roadspace>> =
9699
opendriveModel.roadAsNonEmptyList.map {
97-
roadspaceBuilder.buildRoadspace(it).also { progressBar.step() }
100+
roadspaceBuilder.buildRoadspace(it, roadMarkRepresentationRegistry).also { progressBar.step() }
98101
}
99102

100103
@OptIn(DelicateCoroutinesApi::class)
101104
private fun transformRoadspacesConcurrently(
102105
opendriveModel: OpendriveModel,
106+
roadMarkRepresentationRegistry: RoadMarkRepresentationRegistry,
103107
progressBar: ProgressBar,
104108
): List<ContextIssueList<Roadspace>> {
105109
val roadspacesDeferred =
106110
opendriveModel.roadAsNonEmptyList.map {
107111
GlobalScope.async {
108-
roadspaceBuilder.buildRoadspace(it).also { progressBar.step() }
112+
roadspaceBuilder.buildRoadspace(it, roadMarkRepresentationRegistry).also { progressBar.step() }
109113
}
110114
}
111115
return runBlocking { roadspacesDeferred.map { it.await() } }

0 commit comments

Comments
 (0)