From 408759c5e345cd35658c791a142b74acaeba2330 Mon Sep 17 00:00:00 2001 From: Benedikt Schwab Date: Mon, 11 Sep 2023 13:19:04 +0200 Subject: [PATCH] added lane change relation creation --- .../roadspaces/identifier/LaneIdentifier.kt | 6 +++ .../model/roadspaces/identifier/RoadSide.kt | 29 +++++++++++++ .../roadspaces/LaneBuilder.kt | 2 +- .../Roadspaces2CitygmlTransformer.kt | 42 ++++++++++++++----- .../module/RelationAdder.kt | 19 ++++++--- 5 files changed, 82 insertions(+), 16 deletions(-) create mode 100644 rtron-model/src/main/kotlin/io/rtron/model/roadspaces/identifier/RoadSide.kt diff --git a/rtron-model/src/main/kotlin/io/rtron/model/roadspaces/identifier/LaneIdentifier.kt b/rtron-model/src/main/kotlin/io/rtron/model/roadspaces/identifier/LaneIdentifier.kt index 8f3c0961..2ff4e8b6 100644 --- a/rtron-model/src/main/kotlin/io/rtron/model/roadspaces/identifier/LaneIdentifier.kt +++ b/rtron-model/src/main/kotlin/io/rtron/model/roadspaces/identifier/LaneIdentifier.kt @@ -36,6 +36,12 @@ data class LaneIdentifier( val hashKey get() = "Lane_${laneId}_${laneSectionIdentifier.laneSectionId}_${laneSectionIdentifier.roadspaceIdentifier.roadspaceId}" // Methods + fun getRoadSide(): RoadSide = when { + laneId > 0 -> RoadSide.LEFT + laneId == 0 -> RoadSide.CENTER + else -> RoadSide.RIGHT + } + fun isLeft() = laneId > 0 fun isCenter() = laneId == 0 fun isRight() = laneId < 0 diff --git a/rtron-model/src/main/kotlin/io/rtron/model/roadspaces/identifier/RoadSide.kt b/rtron-model/src/main/kotlin/io/rtron/model/roadspaces/identifier/RoadSide.kt new file mode 100644 index 00000000..197f7801 --- /dev/null +++ b/rtron-model/src/main/kotlin/io/rtron/model/roadspaces/identifier/RoadSide.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2019-2023 Chair of Geoinformatics, Technical University of Munich + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.rtron.model.roadspaces.identifier + +enum class RoadSide { + LEFT, + CENTER, + RIGHT +} + +fun RoadSide.opposite() = when (this) { + RoadSide.LEFT -> RoadSide.RIGHT + RoadSide.CENTER -> RoadSide.CENTER + RoadSide.RIGHT -> RoadSide.LEFT +} diff --git a/rtron-transformer/src/main/kotlin/io/rtron/transformer/converter/opendrive2roadspaces/roadspaces/LaneBuilder.kt b/rtron-transformer/src/main/kotlin/io/rtron/transformer/converter/opendrive2roadspaces/roadspaces/LaneBuilder.kt index b8a114df..52a97e51 100644 --- a/rtron-transformer/src/main/kotlin/io/rtron/transformer/converter/opendrive2roadspaces/roadspaces/LaneBuilder.kt +++ b/rtron-transformer/src/main/kotlin/io/rtron/transformer/converter/opendrive2roadspaces/roadspaces/LaneBuilder.kt @@ -223,7 +223,7 @@ class LaneBuilder( attribute("_width", roadMark.width) attribute("_type", roadMark.typeAttribute.toString()) attribute("_weight", roadMark.weight.map { it.toString() }) - attribute("_laneChange", roadMark.laneChange.getOrElse { ERoadLanesLaneSectionLCRLaneRoadMarkLaneChange.BOTH }.toString()) + attribute("_laneChange", roadMark.laneChange.map { it.toString() }) attribute("_color", roadMark.color.toString()) attribute("_material", roadMark.material) } diff --git a/rtron-transformer/src/main/kotlin/io/rtron/transformer/converter/roadspaces2citygml/Roadspaces2CitygmlTransformer.kt b/rtron-transformer/src/main/kotlin/io/rtron/transformer/converter/roadspaces2citygml/Roadspaces2CitygmlTransformer.kt index 094a7946..76bfa953 100644 --- a/rtron-transformer/src/main/kotlin/io/rtron/transformer/converter/roadspaces2citygml/Roadspaces2CitygmlTransformer.kt +++ b/rtron-transformer/src/main/kotlin/io/rtron/transformer/converter/roadspaces2citygml/Roadspaces2CitygmlTransformer.kt @@ -27,7 +27,11 @@ import io.rtron.io.messages.mergeMessageLists import io.rtron.math.projection.CoordinateReferenceSystem import io.rtron.model.citygml.CitygmlModel import io.rtron.model.roadspaces.RoadspacesModel +import io.rtron.model.roadspaces.identifier.opposite +import io.rtron.model.roadspaces.roadspace.road.Lane +import io.rtron.model.roadspaces.roadspace.road.LaneChange import io.rtron.std.getValueEither +import io.rtron.transformer.converter.roadspaces2citygml.module.RelationAdder import io.rtron.transformer.converter.roadspaces2citygml.report.Roadspaces2CitygmlReport import io.rtron.transformer.converter.roadspaces2citygml.transformer.RoadsTransformer import io.rtron.transformer.converter.roadspaces2citygml.transformer.RoadspaceObjectTransformer @@ -38,6 +42,7 @@ import kotlinx.coroutines.runBlocking import mu.KotlinLogging import org.citygml4j.core.model.core.AbstractCityObject import org.citygml4j.core.model.transportation.Road +import org.citygml4j.core.model.transportation.TrafficSpaceProperty import org.citygml4j.core.model.transportation.TrafficSpaceReference import org.xmlobjects.gml.model.feature.BoundingShape import org.xmlobjects.gml.model.geometry.Envelope @@ -56,6 +61,7 @@ class Roadspaces2CitygmlTransformer( private val roadObjectTransformer = RoadspaceObjectTransformer(parameters) private val roadLanesTransformer = RoadsTransformer(parameters) + private val relationAdder = RelationAdder(parameters) // Methods @@ -174,11 +180,15 @@ class Roadspaces2CitygmlTransformer( dstTransportationSpaces.flatMap { it.sections }.filter { it.isSetObject }.flatMap { it.`object`.trafficSpaces } + dstTransportationSpaces.flatMap { it.intersections }.filter { it.isSetObject }.flatMap { it.`object`.trafficSpaces } // TODO: trace the traffic space created without id - val trafficSpacePropertiesAdjusted = trafficSpaceProperties.filter { it.`object`.id != null } + val trafficSpacePropertyMap: Map = trafficSpaceProperties + .filter { it.`object`.id != null } + .associateBy { it.`object`.id } - val lanesMap = roadspacesModel.getAllLeftRightLanes().associateBy { it.id.deriveTrafficSpaceOrAuxiliaryTrafficSpaceGmlIdentifier(parameters.gmlIdPrefix) } - trafficSpacePropertiesAdjusted.forEach { currentTrafficSpace -> - val currentLane = lanesMap.getValueEither(currentTrafficSpace.`object`.id).getOrElse { return@forEach } + val lanesMap: Map = roadspacesModel + .getAllLeftRightLanes() + .associateBy { it.id.deriveTrafficSpaceOrAuxiliaryTrafficSpaceGmlIdentifier(parameters.gmlIdPrefix) } + trafficSpacePropertyMap.values.forEach { currentTrafficSpace -> + val currentLane: Lane = lanesMap.getValueEither(currentTrafficSpace.`object`.id).getOrElse { return@forEach } // predecessor val predecessorLaneIds = @@ -200,15 +210,27 @@ class Roadspaces2CitygmlTransformer( currentTrafficSpace.`object`.successors = successorLaneIds .map { TrafficSpaceReference(parameters.xlinkPrefix + it.deriveTrafficSpaceOrAuxiliaryTrafficSpaceGmlIdentifier(parameters.gmlIdPrefix)) } - // lane - - val a = currentLane.getLaneChange() + // lateral lane changes val outerLaneId = currentLane.id.getAdjacentOuterLaneIdentifier() val outerLaneGmlId = outerLaneId.deriveTrafficSpaceOrAuxiliaryTrafficSpaceGmlIdentifier(parameters.gmlIdPrefix) - lanesMap[outerLaneGmlId].toOption().tap { outerLane -> - } + val outerLaneOptional = lanesMap[outerLaneGmlId].toOption() + val outerTrafficSpaceOptional = trafficSpacePropertyMap[outerLaneGmlId].toOption() + + if (outerLaneOptional.isDefined() && outerTrafficSpaceOptional.isDefined()) { + val outerLane = outerLaneOptional.getOrElse { throw IllegalStateException("OuterLane must exist.") } + val outerTrafficSpace = outerTrafficSpaceOptional.getOrElse { throw IllegalStateException("OuterTrafficSpace must exist") } + + val laneChangeType = currentLane.getLaneChange().getOrElse { LaneChange.BOTH } - // if (currentLane.id.isRight()) + val laneChangeDirection = currentLane.id.getRoadSide() + + if (laneChangeType == LaneChange.BOTH || laneChangeType == LaneChange.INCREASE) { + relationAdder.addLaneChangeRelation(outerLane, laneChangeDirection, currentTrafficSpace.`object`) + } + if (laneChangeType == LaneChange.BOTH || laneChangeType == LaneChange.DECREASE) { + relationAdder.addLaneChangeRelation(currentLane, laneChangeDirection.opposite(), outerTrafficSpace.`object`) + } + } } } diff --git a/rtron-transformer/src/main/kotlin/io/rtron/transformer/converter/roadspaces2citygml/module/RelationAdder.kt b/rtron-transformer/src/main/kotlin/io/rtron/transformer/converter/roadspaces2citygml/module/RelationAdder.kt index 6801ac17..e135178c 100644 --- a/rtron-transformer/src/main/kotlin/io/rtron/transformer/converter/roadspaces2citygml/module/RelationAdder.kt +++ b/rtron-transformer/src/main/kotlin/io/rtron/transformer/converter/roadspaces2citygml/module/RelationAdder.kt @@ -18,6 +18,7 @@ package io.rtron.transformer.converter.roadspaces2citygml.module import io.rtron.model.roadspaces.identifier.AbstractRoadspacesIdentifier import io.rtron.model.roadspaces.identifier.LaneIdentifier +import io.rtron.model.roadspaces.identifier.RoadSide import io.rtron.model.roadspaces.roadspace.objects.RoadspaceObject import io.rtron.model.roadspaces.roadspace.road.Lane import io.rtron.transformer.converter.roadspaces2citygml.Roadspaces2CitygmlParameters @@ -27,6 +28,7 @@ import io.rtron.transformer.converter.roadspaces2citygml.transformer.deriveTraff import org.citygml4j.core.model.core.AbstractCityObject import org.citygml4j.core.model.core.CityObjectRelation import org.citygml4j.core.model.core.CityObjectRelationProperty +import org.citygml4j.core.model.transportation.TrafficSpace import org.xmlobjects.gml.model.basictypes.Code /** @@ -58,11 +60,18 @@ class RelationAdder( dstCityObject.relatedTo = roadspaceObject.laneRelations.flatMap { it.getAllLeftRightLaneIdentifiers() }.map { createCityObjectRelation(it.deriveTrafficSpaceOrAuxiliaryTrafficSpaceGmlIdentifier(parameters.gmlIdPrefix), "belongsTo", it) } } - fun addLaneChangeRelation(lane: Lane, dstCityObject: AbstractCityObject) { - // val a = lane.id.getAdjacentOuterLaneIdentifier() - - // lane.roadMarkings.first(). - // createCityObjectRelation() + /** + * Adds a lane change relation to the [dstTrafficSpace] object + */ + fun addLaneChangeRelation(lane: Lane, direction: RoadSide, dstTrafficSpace: TrafficSpace) { + val gmlId = lane.id.deriveTrafficSpaceOrAuxiliaryTrafficSpaceGmlIdentifier(parameters.gmlIdPrefix) + val relationType = when (direction) { + RoadSide.LEFT -> "leftLaneChange" + RoadSide.RIGHT -> "rightLaneChange" + RoadSide.CENTER -> throw IllegalArgumentException("Direction of a laneChange relation must not be center.") + } + val relation: CityObjectRelationProperty = createCityObjectRelation(gmlId, relationType, lane.id) + dstTrafficSpace.relatedTo.add(relation) } private fun createCityObjectRelation(gmlId: String, type: String, id: AbstractRoadspacesIdentifier): CityObjectRelationProperty {