Issue #45: add more ways to draw roads.

This commit is contained in:
Sergey Vartanov 2021-06-05 23:37:30 +03:00
parent 9b807c2cbf
commit 73f2c467be

View file

@ -1,5 +1,5 @@
""" """
Road shape drawing. WIP: road shape drawing.
""" """
from dataclasses import dataclass from dataclasses import dataclass
from typing import List, Optional from typing import List, Optional
@ -29,6 +29,14 @@ class Lane:
def set_forward(self, is_forward: bool) -> None: def set_forward(self, is_forward: bool) -> None:
self.is_forward = is_forward self.is_forward = is_forward
def get_width(self, scale: float):
"""
Get lane width. We use standard 3.7 m lane.
"""
if self.width is None:
return 3.7 * scale
return self.width * scale
class RoadPart: class RoadPart:
""" """
@ -39,22 +47,23 @@ class RoadPart:
self, self,
point_1: np.array, point_1: np.array,
point_2: np.array, point_2: np.array,
left_offset: float,
right_offset: float,
lanes: List[Lane], lanes: List[Lane],
scale: False,
): ):
""" """
:param point_1: start point of the road part :param point_1: start point of the road part
:param point_2: end point of the road part :param point_2: end point of the road part
:param left_offset: offset from the central line to the left border
:param right_offset: offset from the central line to the right border
:param lanes: lane specification :param lanes: lane specification
""" """
self.point_1: np.array = point_1 self.point_1: np.array = point_1
self.point_2: np.array = point_2 self.point_2: np.array = point_2
self.left_offset: float = left_offset
self.right_offset: float = right_offset
self.lanes: List[Lane] = lanes self.lanes: List[Lane] = lanes
if lanes:
self.width = sum(map(lambda x: x.get_width(scale), lanes))
else:
self.width = 1
self.left_offset: float = self.width / 2
self.right_offset: float = self.width / 2
self.turned: np.array = norm( self.turned: np.array = norm(
turn_by_angle(self.point_2 - self.point_1, np.pi / 2) turn_by_angle(self.point_2 - self.point_1, np.pi / 2)
@ -74,20 +83,18 @@ class RoadPart:
node_2: OSMNode, node_2: OSMNode,
flinger: Flinger, flinger: Flinger,
road, road,
scale,
) -> "RoadPart": ) -> "RoadPart":
""" """
Construct road part from OSM nodes. Construct road part from OSM nodes.
""" """
left_offset: float = road.width / 2
right_offset: float = road.width / 2
lanes = [Lane(road.width / road.lanes)] * road.lanes lanes = [Lane(road.width / road.lanes)] * road.lanes
return cls( return cls(
flinger.fling(node_1.coordinates), flinger.fling(node_1.coordinates),
flinger.fling(node_2.coordinates), flinger.fling(node_2.coordinates),
left_offset,
right_offset,
lanes, lanes,
scale,
) )
def update(self) -> None: def update(self) -> None:
@ -104,12 +111,24 @@ class RoadPart:
self.left_projection = ( self.left_projection = (
self.right_connection - self.right_vector + self.left_vector self.right_connection - self.right_vector + self.left_vector
) )
a = np.linalg.norm(self.right_connection - self.point_2) a = np.linalg.norm(self.right_connection - self.point_1)
b = np.linalg.norm(self.right_projection - self.point_2) b = np.linalg.norm(self.right_projection - self.point_1)
if a < b: if a > b:
self.point_middle = self.right_connection - self.right_vector self.right_outer = self.right_connection
self.left_outer = self.left_projection
else: else:
self.point_middle = self.right_projection - self.right_vector self.right_outer = self.right_projection
self.left_outer = self.left_connection
self.point_middle = self.right_outer - self.right_vector
max_: float = 100
if np.linalg.norm(self.point_middle - self.point_1) > max_:
self.point_a = self.point_1 + max_ * norm(self.point_middle - self.point_1)
self.right_outer = self.point_a + self.right_vector
self.left_outer = self.point_a + self.left_vector
else:
self.point_a = self.point_middle
def get_angle(self) -> float: def get_angle(self) -> float:
""" """
@ -117,6 +136,18 @@ class RoadPart:
""" """
return angle(self.point_2 - self.point_1) return angle(self.point_2 - self.point_1)
def draw_normal(self, drawing: svgwrite.Drawing):
"""
Draw some debug lines.
"""
line = drawing.path(
("M", self.point_1, "L", self.point_2),
fill="none",
stroke="#8888FF",
stroke_width=self.width,
)
drawing.add(line)
def draw_debug(self, drawing: svgwrite.Drawing): def draw_debug(self, drawing: svgwrite.Drawing):
""" """
Draw some debug lines. Draw some debug lines.
@ -148,19 +179,31 @@ class RoadPart:
) )
drawing.add(line) drawing.add(line)
opacity: float = 0.4
radius: float = 2
if self.right_connection is not None: if self.right_connection is not None:
circle = drawing.circle(self.right_connection, 1.2) circle = drawing.circle(self.right_connection, radius, fill="#FF0000", opacity=opacity)
drawing.add(circle) drawing.add(circle)
if self.left_connection is not None: if self.left_connection is not None:
circle = drawing.circle(self.left_connection, 1.2) circle = drawing.circle(self.left_connection, radius, fill="#0000FF", opacity=opacity)
drawing.add(circle) drawing.add(circle)
if self.right_projection is not None: if self.right_projection is not None:
circle = drawing.circle(self.right_projection, 1.2, fill="#FF0000") circle = drawing.circle(self.right_projection, radius, fill="#FF0000", opacity=opacity)
drawing.add(circle) drawing.add(circle)
if self.left_projection is not None: if self.left_projection is not None:
circle = drawing.circle(self.left_projection, 1.2, fill="#0000FF") circle = drawing.circle(self.left_projection, radius, fill="#0000FF", opacity=opacity)
drawing.add(circle) drawing.add(circle)
circle = drawing.circle(self.point_middle, 1.2, fill="#000000")
if self.right_projection is not None:
circle = drawing.circle(self.right_outer, radius, fill="#FF0000", opacity=opacity)
drawing.add(circle)
if self.left_projection is not None:
circle = drawing.circle(self.left_outer, radius, fill="#0000FF", opacity=opacity)
drawing.add(circle)
circle = drawing.circle(self.point_a, radius, fill="#000000")
drawing.add(circle) drawing.add(circle)
self.draw_entrance(drawing, True) self.draw_entrance(drawing, True)
@ -178,7 +221,7 @@ class RoadPart:
] ]
drawing.add(drawing.path(path_commands, fill="#CCCCCC")) drawing.add(drawing.path(path_commands, fill="#CCCCCC"))
def draw_entrance(self, drawing: svgwrite.Drawing, is_debug: bool): def draw_entrance(self, drawing: svgwrite.Drawing, is_debug: bool = False):
""" """
Draw intersection entrance part. Draw intersection entrance part.
""" """
@ -195,16 +238,16 @@ class RoadPart:
) )
drawing.add(path) drawing.add(path)
else: else:
drawing.add(drawing.path(path_commands, fill="#BBBBBB")) drawing.add(drawing.path(path_commands, fill="#88FF88"))
def draw_lanes(self, drawing: svgwrite.Drawing): def draw_lanes(self, drawing: svgwrite.Drawing, scale: float):
""" """
Draw lane delimiters. Draw lane delimiters.
""" """
for lane in self.lanes: for lane in self.lanes:
a = self.right_vector - self.turned * lane.width shift = self.right_vector - self.turned * lane.get_width(scale)
path = drawing.path( path = drawing.path(
["M", self.point_middle + a, "L", self.point_2 + a], ["M", self.point_middle + shift, "L", self.point_2 + shift],
fill="none", fill="none",
stroke="#FFFFFF", stroke="#FFFFFF",
stroke_width=2, stroke_width=2,
@ -240,24 +283,37 @@ class Intersection:
part_2.left_connection = intersection part_2.left_connection = intersection
part_2.update() part_2.update()
def draw(self, drawing: svgwrite.Drawing, is_debug: bool = False): def draw(
self, drawing: svgwrite.Drawing, scale: float, is_debug: bool = False
) -> None:
""" """
Draw all road parts and intersection. Draw all road parts and intersection.
""" """
path_commands = ["M"] inner_commands = ["M"]
for part in self.parts: for part in self.parts:
path_commands += [part.left_connection, "L"] inner_commands += [part.left_connection, "L"]
path_commands[-1] = "Z" inner_commands[-1] = "Z"
outer_commands = ["M"]
for part in self.parts:
outer_commands += [part.left_connection, "L"]
outer_commands += [part.left_outer, "L"]
outer_commands += [part.right_outer, "L"]
outer_commands[-1] = "Z"
# for part in self.parts:
# part.draw_normal(drawing)
if is_debug: if is_debug:
drawing.add(drawing.path(path_commands, fill="#DDFFDD")) drawing.add(drawing.path(outer_commands, fill="#0000FF", opacity=0.2))
drawing.add(drawing.path(inner_commands, fill="#FF0000", opacity=0.2))
for part in self.parts: for part in self.parts:
if is_debug: if is_debug:
part.draw_debug(drawing) part.draw_debug(drawing)
else: else:
part.draw(drawing) part.draw_entrance(drawing)
if not is_debug: if not is_debug:
for part in self.parts: # for part in self.parts:
part.draw_lanes(drawing) # part.draw_lanes(drawing, scale)
drawing.add(drawing.path(path_commands, fill="#CCCCCC")) drawing.add(drawing.path(inner_commands, fill="#FF8888"))