mirror of
https://github.com/enzet/map-machine.git
synced 2025-06-05 20:31:51 +02:00
Issue #45: add more ways to draw roads.
This commit is contained in:
parent
9b807c2cbf
commit
73f2c467be
1 changed files with 91 additions and 35 deletions
126
roentgen/road.py
126
roentgen/road.py
|
@ -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"))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue