2This script creates a vector map with two parallel lanes using the Lanelet2 library and saves it as an OSM file.
3The script can be run from the command line with the following arguments:
4- filename: The output filename for the vector map.
5- total_length: The length of the lanes (default is 50.0).
6- lane_width: The width of the lanes (default is 3.7).
7- points_per_meter: The number of points per meter (default is 5).
10- lanelet2: The Lanelet2 library for handling lanelet maps.
11- argparse: For parsing command line arguments.
13 python3 create_two_lane_map.py --filename output.osm --total_length <total_length> --lane_width <lane_width> --points_per_meter <points_per_meter>
16from pyproj import Proj, Transformer
17import xml.etree.ElementTree as ET
18from xml.dom import minidom
22# Adjusted geoReference (removed vertical geoid grid)
23geo_reference = "+proj=tmerc +lat_0=0 +lon_0=0 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs"
25# Coordinate transformer (meters → lat/lon)
26proj = Proj(geo_reference)
27transformer = Transformer.from_proj(proj, "epsg:4326", always_xy=True)
33nodes, ways, relations = [], [], []
35def add_node(x, y, z=0.0):
37 lon, lat = transformer.transform(x, y)
38 node = ET.Element(
"node", id=
str(node_id), version=
"1", lat=f
"{lat:.9f}", lon=f
"{lon:.9f}", visible=
"true")
39 ET.SubElement(node,
"tag", k=
"ele", v=f
"{z:.2f}")
40 ET.SubElement(node,
"tag", k=
"lat", v=f
"{lat:.9f}")
41 ET.SubElement(node,
"tag", k=
"lon", v=f
"{lon:.9f}")
42 nodes.append((node_id, node))
48 way = ET.Element(
"way", id=
str(way_id), version=
"1", visible=
"true")
50 ET.SubElement(way,
"nd", ref=
str(nid))
51 for k, v
in tags.items():
52 ET.SubElement(way,
"tag", k=k, v=v)
53 ways.append((way_id, way))
59 rel = ET.Element(
"relation", id=
str(relation_id), version=
"1", visible=
"true")
60 ET.SubElement(rel,
"member", type=
"way", ref=
str(left_id), role=
"left")
61 ET.SubElement(rel,
"member", type=
"way", ref=
str(right_id), role=
"right")
62 for k, v
in tags.items():
63 ET.SubElement(rel,
"tag", k=k, v=v)
64 ET.SubElement(rel,
"tag", k=
"cad_id", v=
str(relation_id))
65 relations.append((relation_id, rel))
70 Create a vector map with two parallel lanes.
72 - filename: The output filename
for the vector map.
73 - total_length: The length of the lanes.
74 - lane_width: The width of the lanes.
75 - points_per_meter: The number of points per meter.
79 total_points =
int(total_length * points_per_meter) + 1
80 points_per_lanelet =
int(lanelet_length * points_per_meter) + 1
82 x_offset = -total_length / 2
83 y_offset = -lane_width
84 left1, right1, left2, right2 = [], [], [], []
85 for i
in range(total_points):
86 x = i / points_per_meter + x_offset
87 right1.append(
add_node(x, 0 + y_offset))
88 left1.append(
add_node(x, lane_width + y_offset))
89 right2.append(
add_node(x, lane_width + y_offset))
90 left2.append(
add_node(x, 2 * lane_width + y_offset))
92 stride = points_per_lanelet - 1
93 way_dict = {
"type":
"line_thin",
"subtype":
"solid"}
94 lanelet_dict = {
"type":
"lanelet",
97 "turn_direction":
"straight",
99 "direction" :
"ONE_WAY",
101 "location" :
"private",
103 "participant:vehicle" :
"yes",
106 for i
in range(0, total_points - stride, stride):
108 l2_nodes = left2[i:i + points_per_lanelet]
112 center_nodes = right2[i:i + points_per_lanelet]
113 center_id =
create_way(center_nodes, {
"type":
"line_thin",
"subtype":
"dashed"})
116 r1_nodes = right1[i:i + points_per_lanelet]
126 osm = ET.Element(
"osm", version=
"0.6")
127 ET.SubElement(osm,
"geoReference", v=geo_reference)
128 for _, node
in nodes:
132 for _, rel
in relations:
136 with open(filename,
"w", encoding=
"utf-8")
as f:
137 xml_str = minidom.parseString(ET.tostring(osm)).toprettyxml(indent=
" ")
140 print(f
"Map saved to {filename}")
142if __name__ ==
"__main__":
143 parser = argparse.ArgumentParser(description=
"Create a vector map with two parallel lanes.")
144 parser.add_argument(
"--filename", type=str, default=
"two_lane_straight.osm", help=
"Output filename for the vector map.")
145 parser.add_argument(
"--total_length", type=float, default=50.0, help=
"Length of the lanes.")
146 parser.add_argument(
"--lane_width", type=float, default=3.7, help=
"Width of the lanes.")
147 parser.add_argument(
"--points_per_meter", type=int, default=5, help=
"Number of points per meter.")
148 args = parser.parse_args()
149 create_vector_map(args.filename, args.total_length, args.lane_width, args.points_per_meter)
150 print(
"Vector map with two parallel lanes created successfully.")
def create_way(node_ids, tags)
def create_lanelet(left_id, right_id, tags)
def create_vector_map(filename, total_length, lane_width, points_per_meter)
def add_node(x, y, z=0.0)