Skip to content

Commit 2543f16

Browse files
allow osi lanes to only have centerlines
1 parent b3d26e3 commit 2543f16

File tree

6 files changed

+86
-41
lines changed

6 files changed

+86
-41
lines changed

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
<img src="https://www.ika.rwth-aachen.de/images/ika-logo-a-blau-blau-rgb.svg" align="right" width="240">
1+
2+
<img src="https://www.ika.rwth-aachen.de/images/ika-logo-a-blau-blau-rgb.svg" align="right" width="240">
3+
4+
[![](https://img.shields.io/badge/license-MPL%202.0-blue.svg)](https://github.com/ika-rwth-aachen/omega-prime/blob/master/LICENSE)
5+
[![](https://img.shields.io/pypi/v/omega-prime.svg)](https://pypi.python.org/pypi/omega-prime)
6+
[![](https://github.com/ika-rwth-aachen/omega-prime/workflows/CI/badge.svg)](https://github.com/ika-rwth-aachen/omega-prime/actions)
7+
[![](https://img.shields.io/pypi/pyversions/omega-prime.svg)](https://pypi.python.org/pypi/omega-prime/)
8+
[![](https://img.shields.io/github/issues-raw/ika-rwth-aachen/omega-prime.svg)](https://github.com/ika-rwth-aachen/omega-prime/issues)
9+
210
</br>
311
</br>
412

@@ -7,6 +15,7 @@
715
816

917
# Omega-Prime: Data Model, Data Format and Python Library for Handling Ground Truth Traffic Data
18+
1019
Data Model, Format and Python Library for ground truth data containing information on dynamic objects, map and environmental factors optimized for representing urban traffic. The repository contains:
1120
- **Sepcification Document:** to be released
1221
- **Data Model**: What signals exists and how these are defined.

omega_prime/asam_odr/opendriveconverter/converter.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,8 @@
1-
from dataclasses import dataclass
2-
from typing import Any
3-
41
from ..logger import logger
52
from .elements.junction import setup_connections
63
from .elements.road import process_road
74

85

9-
@dataclass(repr=False)
10-
class Map:
11-
roads: dict
12-
georeference: Any = None
13-
14-
156
def convert_opendrive(my_opendrive, step_size=0.1) -> tuple[dict, tuple[float, float, str]]:
167
"""
178
Converts the opendrive XML input into a OMEGAFormat road network

omega_prime/map.py

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,19 @@ def get_osi(self) -> betterosi.LaneBoundary:
5353

5454

5555
@dataclass(repr=False)
56-
class Lane:
56+
class LaneBase:
5757
_map: "Map" = field(init=False)
5858
idx: Any
5959
centerline: np.ndarray
6060
type: betterosi.LaneClassificationType
6161
subtype: betterosi.LaneClassificationSubtype
6262
successor_ids: list[Any]
6363
predecessor_ids: list[Any]
64+
source_reference: Any = field(init=False)
65+
66+
67+
@dataclass(repr=False)
68+
class Lane(LaneBase):
6469
right_boundary_id: Any
6570
left_boundary_id: Any
6671
polygon: shapely.Polygon = field(init=False)
@@ -70,11 +75,8 @@ class Lane:
7075

7176

7277
@dataclass(repr=False)
73-
class LaneOsi(Lane):
78+
class LaneOsiCenterline(LaneBase):
7479
_osi: betterosi.Lane
75-
right_boundary_ids: list[int]
76-
left_boundary_ids: list[int]
77-
free_boundary_ids: list[int]
7880

7981
@staticmethod
8082
def _get_centerline(lane: betterosi.Lane):
@@ -83,6 +85,31 @@ def _get_centerline(lane: betterosi.Lane):
8385
cl = np.flip(cl, axis=0)
8486
return cl
8587

88+
@classmethod
89+
def create(cls, lane: betterosi.Lane):
90+
return cls(
91+
_osi=lane,
92+
idx=lane.id.value,
93+
centerline=cls._get_centerline(lane),
94+
type=betterosi.LaneClassificationType(lane.classification.type),
95+
subtype=betterosi.LaneClassificationSubtype(lane.classification.subtype),
96+
successor_ids=[],
97+
predecessor_ids=[],
98+
right_boundary_id=None,
99+
left_boundary_id=None,
100+
)
101+
102+
def plot(self, ax: plt.Axes):
103+
c = "black" if not self.type == betterosi.LaneClassificationType.TYPE_INTERSECTION else "green"
104+
ax.plot(*np.array(self.centerline).T, color=c, alpha=0.3, zorder=-10)
105+
106+
107+
@dataclass(repr=False)
108+
class LaneOsi(Lane, LaneOsiCenterline):
109+
right_boundary_ids: list[int]
110+
left_boundary_ids: list[int]
111+
free_boundary_ids: list[int]
112+
86113
@classmethod
87114
def create(cls, lane: betterosi.Lane):
88115
return cls(
@@ -133,7 +160,6 @@ def plot(self, ax: plt.Axes):
133160
ax.add_patch(PltPolygon(self.polygon.exterior.coords, fc="blue", alpha=0.2, ec="black"))
134161

135162
# for ase_engine/omega_prime
136-
137163
def _get_oriented_borders(self):
138164
center_start = shapely.LineString(self.centerline).interpolate(0, normalized=True)
139165
left = self.left_boundary.polyline
@@ -175,9 +201,8 @@ class MapOsi(Map):
175201

176202
@classmethod
177203
def create(cls, gt: betterosi.GroundTruth):
178-
if not hasattr(gt, "lane") or not hasattr(gt, "lane_boundary"):
179-
return None
180-
204+
if len(gt.lane_boundary) == 0:
205+
raise RuntimeError("Empty Map")
181206
return cls(
182207
_osi=gt,
183208
lane_boundaries={b.id.value: LaneBoundaryOsi.create(b) for b in gt.lane_boundary},
@@ -194,3 +219,23 @@ def setup_lanes_and_boundaries(self):
194219
l._map = self
195220
l.set_boundaries()
196221
l.set_polygon()
222+
223+
224+
@dataclass(repr=False)
225+
class MapOsiCenterline(Map):
226+
_osi: betterosi.GroundTruth
227+
lanes: dict[int, LaneOsiCenterline]
228+
229+
@classmethod
230+
def create(cls, gt: betterosi.GroundTruth):
231+
if len(gt.lane) == 0:
232+
raise RuntimeError("No Map")
233+
return cls(
234+
_osi=gt,
235+
lanes={l.id.value: LaneOsiCenterline.create(l) for l in gt.lane},
236+
lane_boundaries={},
237+
)
238+
239+
def setup_lanes_and_boundaries(self):
240+
for l in self.lanes.values():
241+
l._map = self

omega_prime/recording.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
import pandera.extensions as extensions
1515

1616
from .asam_odr import MapOdr
17-
from .map import MapOsi, ProjectionOffset
17+
from .map import MapOsi, ProjectionOffset, MapOsiCenterline
18+
import itertools
1819

1920
pi_valued = pa.Check.between(-np.pi, np.pi)
2021

@@ -292,15 +293,14 @@ def to_osi_gts(self) -> list[betterosi.GroundTruth]:
292293
for nanos, group_df in self._df.groupby("total_nanos")
293294
]
294295

295-
if self.map is not None and isinstance(self.map, MapOsi):
296+
if self.map is not None and isinstance(self.map, MapOsi | MapOsiCenterline):
296297
gts[0].lane_boundary = [b._osi for b in self.map.lane_boundaries.values()]
297298
gts[0].lane = [l._osi for l in self.map.lanes.values()]
298299
return gts
299300

300301
@classmethod
301302
def from_osi_gts(cls, gts: list[betterosi.GroundTruth], validate: bool = True):
302303
mvs = []
303-
map = None
304304
projs = []
305305
for i, gt in enumerate(gts):
306306
total_nanos = gt.timestamp.seconds * 1_000_000_000 + gt.timestamp.nanos
@@ -351,29 +351,37 @@ def from_osi_gts(cls, gts: list[betterosi.GroundTruth], validate: bool = True):
351351
)
352352
for mv in gt.moving_object
353353
]
354-
355-
if map is None:
356-
map = MapOsi.create(gt)
357354
df_mv = pd.DataFrame(mvs).sort_values(by=["total_nanos", "idx"]).reset_index(drop="index")
358-
return cls(df_mv, map, projections=projs, validate=validate)
355+
return cls(df_mv, projections=projs, validate=validate)
359356

360357
@classmethod
361358
def from_file(cls, filepath, xodr_path: str | None = None, validate: bool = True, skip_odr_parse: bool = False):
362359
gts = betterosi.read(
363360
filepath,
364361
return_ground_truth=True,
365362
)
363+
gts, tmp_gts = itertools.tee(gts, 2)
364+
first_gt = next(tmp_gts)
366365
r = cls.from_osi_gts(gts, validate=validate)
367366
if xodr_path is not None:
368367
r.map = MapOdr.from_file(xodr_path, skip_parse=skip_odr_parse)
369368
elif Path(filepath).suffix == ".mcap":
370369
try:
371370
r.map = MapOdr.from_file(filepath, skip_parse=skip_odr_parse)
372371
except StopIteration:
373-
if r.map is not None:
372+
pass
373+
if r.map is None:
374+
try:
375+
r.map = MapOsi.create(first_gt)
376+
warn("No map provided in mcap. OSI GroundTruth map is used!")
377+
except RuntimeError:
378+
try:
379+
r.map = MapOsiCenterline.create(first_gt)
374380
warn("No map provided in mcap. OSI GroundTruth map is used!")
381+
except RuntimeError:
382+
pass
375383
if r.map is None:
376-
warn("No xodr map provided in mcap nor OSI map in GroundTruth!")
384+
warn("No xodr map provided in MCAP nor OSI map in GroundTruth!")
377385
return r
378386

379387
def to_mcap(self, filepath):

pyproject.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ keywords = ["omega-format", "osi", "open-simulation-interface", "simulation", "a
1515
classifiers = [
1616
"License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)",
1717
"Programming Language :: Python :: 3",
18-
"Programming Language :: Python :: 3.9",
1918
"Programming Language :: Python :: 3.10",
2019
"Programming Language :: Python :: 3.11",
2120
"Programming Language :: Python :: 3.12",

0 commit comments

Comments
 (0)