Skip to content

Commit d652863

Browse files
add lanelet2 Map
1 parent 5db8d4a commit d652863

File tree

1 file changed

+178
-0
lines changed

1 file changed

+178
-0
lines changed

omega_prime/map_lanelet.py

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
from typing import Self
2+
import omega_prime
3+
import betterosi
4+
from warnings import warn
5+
import lanelet2
6+
from tqdm.auto import tqdm
7+
import shapely
8+
import numpy as np
9+
from dataclasses import dataclass, field
10+
11+
12+
lst = betterosi.LaneClassificationSubtype
13+
lt = betterosi.LaneClassificationType
14+
15+
lanelet2lst = {
16+
"road": lst.SUBTYPE_NORMAL,
17+
"highway": lst.SUBTYPE_NORMAL,
18+
"play_street": lst.SUBTYPE_NORMAL,
19+
"emergency_lane": lst.SUBTYPE_RESTRICTED,
20+
"bus_lane": lst.SUBTYPE_RESTRICTED,
21+
"bicycle_lane": lst.SUBTYPE_BIKING,
22+
"walkway": lst.SUBTYPE_SIDEWALK,
23+
"shared_walkway": lst.SUBTYPE_SIDEWALK,
24+
"parking": lst.SUBTYPE_PARKING,
25+
"freespace": lst.SUBTYPE_NORMAL,
26+
"exit": lst.SUBTYPE_EXIT,
27+
"keepout": lst.SUBTYPE_RESTRICTED,
28+
}
29+
30+
lanelet2lt = {
31+
"road": lt.TYPE_DRIVING,
32+
"highway": lt.TYPE_DRIVING,
33+
"play_street": lt.TYPE_DRIVING,
34+
"emergency_lane": lt.TYPE_NONDRIVING,
35+
"bus_lane": lt.TYPE_NONDRIVING,
36+
"bicycle_lane": lt.TYPE_NONDRIVING,
37+
"walkway": lt.TYPE_NONDRIVING,
38+
"shared_walkway": lt.TYPE_NONDRIVING,
39+
"parking": lt.TYPE_DRIVING,
40+
"freespace": lt.TYPE_DRIVING,
41+
"exit": lt.TYPE_DRIVING,
42+
"keepout": lt.TYPE_NONDRIVING,
43+
}
44+
45+
not_lane_subtypes = [
46+
"crosswalk",
47+
"bicycle_parking",
48+
"pedestrian_seat",
49+
"vegetation",
50+
"building",
51+
"park",
52+
"intersection",
53+
]
54+
55+
56+
class RoutingGraph:
57+
def __init__(self, map, location=lanelet2.traffic_rules.Locations.Germany):
58+
self.map = map
59+
self.vehicle = lanelet2.routing.RoutingGraph(
60+
map, lanelet2.traffic_rules.create(location, lanelet2.traffic_rules.Participants.Vehicle)
61+
)
62+
self.pedestrian = lanelet2.routing.RoutingGraph(
63+
map, lanelet2.traffic_rules.create(location, lanelet2.traffic_rules.Participants.Pedestrian)
64+
)
65+
self.cycle = lanelet2.routing.RoutingGraph(
66+
map, lanelet2.traffic_rules.create(location, lanelet2.traffic_rules.Participants.Bicycle)
67+
)
68+
69+
def previous(self, l):
70+
return list(set(self.vehicle.previous(l) + self.pedestrian.previous(l) + self.cycle.previous(l)))
71+
72+
def following(self, l):
73+
return list(set(self.vehicle.following(l) + self.pedestrian.following(l) + self.cycle.following(l)))
74+
75+
76+
@dataclass
77+
class MapLanelet(omega_prime.map.Map):
78+
lanes: dict[int, "LaneLanelet"]
79+
lane_boundaries: dict[int, "LaneBoundaryLanelet"]
80+
lanelet_map: lanelet2.core.LaneletMap
81+
_lanelet_routing: RoutingGraph = field(init=False)
82+
83+
def setup_lanes_and_boundaries(self):
84+
pass
85+
86+
def __post_init__(self):
87+
self._lanelet_routing = RoutingGraph(self.lanelet_map)
88+
89+
@classmethod
90+
def create(cls, path: str) -> Self:
91+
proj = lanelet2.projection.UtmProjector(lanelet2.io.Origin(0, 0))
92+
lanelet_map = lanelet2.io.load(path, proj)
93+
94+
map = cls({}, {}, lanelet_map=lanelet_map)
95+
for l in tqdm(lanelet_map.laneletLayer, desc="Lanes"):
96+
l = LaneLanelet.create(map, l)
97+
if l is not None:
98+
map.lanes[l.idx] = l
99+
for a in tqdm(lanelet_map.areaLayer, desc="Areas"):
100+
a = LaneLanelet.create(map, a)
101+
if a is not None:
102+
map.lanes[a.idx] = a
103+
return map
104+
105+
106+
class LaneBoundaryLanelet(omega_prime.map.LaneBoundary):
107+
idx: int
108+
type: str
109+
110+
@classmethod
111+
def create(cls, map: MapLanelet, leftrightBound: lanelet2.core.LineString3d) -> Self:
112+
if leftrightBound.id in map.lane_boundaries:
113+
return map.lane_boundaries[leftrightBound.id]
114+
else:
115+
b = cls(
116+
idx=leftrightBound.id,
117+
type=dict(leftrightBound.attributes).get("type", ""),
118+
polyline=shapely.LineString(np.array([(p.x, p.y, p.z) for p in leftrightBound])),
119+
)
120+
map.lane_boundaries[b.idx] = b
121+
return b
122+
123+
124+
@dataclass(repr=False)
125+
class LaneLanelet(omega_prime.map.Lane):
126+
idx: int
127+
type: str
128+
subtype: str
129+
polygon: shapely.Polygon
130+
left_boundary: LaneBoundaryLanelet
131+
right_boundary: LaneBoundaryLanelet
132+
133+
@classmethod
134+
def create(cls, map: MapLanelet, obj: lanelet2.core.Area | lanelet2.core.Lanelet) -> Self:
135+
rb = None
136+
lb = None
137+
polygon = None
138+
centerline = None
139+
successor_ids = []
140+
predecessor_ids = []
141+
142+
ltype = dict(obj.attributes).get("type", "")
143+
lsubtype = dict(obj.attributes).get("subtype", "")
144+
if lsubtype in not_lane_subtypes:
145+
return None
146+
type = lanelet2lt.get(lsubtype, None)
147+
subtype = lanelet2lst.get(lsubtype, None)
148+
149+
if type is None or subtype is None:
150+
warn(
151+
f"Lanelet Lane id={obj.id} could not be mapped to omega_prime with type={ltype} and subtype={lsubtype}."
152+
)
153+
return None
154+
155+
if isinstance(obj, lanelet2.core.Lanelet):
156+
lb = LaneBoundaryLanelet.create(map, obj.leftBound)
157+
rb = LaneBoundaryLanelet.create(map, obj.rightBound)
158+
polygon = shapely.Polygon(np.array([(p.x, p.y, p.z) for p in obj.polygon3d()]))
159+
centerline = shapely.LineString(np.array([(p.x, p.y, p.z) for p in obj.centerline]))
160+
successor_ids = (map._lanelet_routing.following(obj),)
161+
predecessor_ids = map._lanelet_routing.previous(obj)
162+
elif isinstance(obj, lanelet2.core.Area):
163+
polygon = shapely.Polygon(np.array([(p.x, p.y, p.z) for p in obj.outerBoundPolygon()]))
164+
for ip in obj.innerBoundPolygons():
165+
polygon = polygon.difference(np.array([(p.x, p.y, p.z) for p in ip]))
166+
return cls(
167+
idx=obj.id,
168+
type=type,
169+
subtype=subtype,
170+
left_boundary_id=lb.idx if lb else None,
171+
right_boundary_id=rb.idx if rb else None,
172+
left_boundary=lb,
173+
right_boundary=rb,
174+
polygon=polygon,
175+
centerline=centerline,
176+
successor_ids=successor_ids,
177+
predecessor_ids=predecessor_ids,
178+
)

0 commit comments

Comments
 (0)