mirror of
https://github.com/enzet/map-machine.git
synced 2025-05-04 20:56:46 +02:00
Issue #83: display transition as connection.
Display road part with placement=transition as connection between two roads.
This commit is contained in:
parent
c3dfaf0604
commit
c193901e15
3 changed files with 95 additions and 64 deletions
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
|
@ -46,7 +46,7 @@ jobs:
|
||||||
pip install .
|
pip install .
|
||||||
- name: Check code style with Black
|
- name: Check code style with Black
|
||||||
run: |
|
run: |
|
||||||
black -l 80 --check map_machine tests
|
black -l 80 --check map_machine setup.py tests
|
||||||
- name: Lint with Flake8
|
- name: Lint with Flake8
|
||||||
run: |
|
run: |
|
||||||
flake8 --max-line-length=80 --ignore=E203,W503
|
flake8 --max-line-length=80 --ignore=E203,W503
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
WIP: road shape drawing.
|
WIP: road shape drawing.
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
from collections import defaultdict
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Any, Optional, Union
|
from typing import Any, Optional, Union
|
||||||
|
|
||||||
|
@ -376,7 +377,7 @@ class Road(Tagged):
|
||||||
self.matcher: RoadMatcher = matcher
|
self.matcher: RoadMatcher = matcher
|
||||||
|
|
||||||
self.line: Polyline = Polyline(
|
self.line: Polyline = Polyline(
|
||||||
[flinger.fling(x.coordinates) for x in self.nodes]
|
[flinger.fling(node.coordinates) for node in self.nodes]
|
||||||
)
|
)
|
||||||
self.width: Optional[float] = matcher.default_width
|
self.width: Optional[float] = matcher.default_width
|
||||||
self.lanes: list[Lane] = []
|
self.lanes: list[Lane] = []
|
||||||
|
@ -420,19 +421,19 @@ class Road(Tagged):
|
||||||
self.layer = float(tags["layer"])
|
self.layer = float(tags["layer"])
|
||||||
|
|
||||||
self.placement_offset: float = 0.0
|
self.placement_offset: float = 0.0
|
||||||
self.transition: bool = False
|
self.is_transition: bool = False
|
||||||
|
|
||||||
if "placement" in tags:
|
if "placement" in tags:
|
||||||
value: str = tags["placement"]
|
value: str = tags["placement"]
|
||||||
if value == "transition":
|
if value == "transition":
|
||||||
self.is_transition = True
|
self.is_transition = True
|
||||||
elif ":" in value and len(parts := value.split(":")) == 2:
|
elif ":" in value and len(parts := value.split(":")) == 2:
|
||||||
place, lane = parts
|
place, lane_string = parts
|
||||||
lane_number: int = int(lane)
|
lane_number: int = int(lane_string)
|
||||||
self.placement_offset = (
|
self.placement_offset = (
|
||||||
sum(
|
sum(
|
||||||
x.get_width(self.scale)
|
lane.get_width(self.scale)
|
||||||
for x in self.lanes[: lane_number - 1]
|
for lane in self.lanes[: lane_number - 1]
|
||||||
)
|
)
|
||||||
- self.width * self.scale / 2
|
- self.width * self.scale / 2
|
||||||
)
|
)
|
||||||
|
@ -629,8 +630,12 @@ class Connector:
|
||||||
self.road_1, self.index_1 = connections[0]
|
self.road_1, self.index_1 = connections[0]
|
||||||
self.priority = self.road_1.matcher.priority
|
self.priority = self.road_1.matcher.priority
|
||||||
|
|
||||||
self.min_layer: float = min(x[0].layer for x in connections)
|
self.min_layer: float = min(
|
||||||
self.max_layer: float = max(x[0].layer for x in connections)
|
connection[0].layer for connection in connections
|
||||||
|
)
|
||||||
|
self.max_layer: float = max(
|
||||||
|
connection[0].layer for connection in connections
|
||||||
|
)
|
||||||
self.scale: float = self.road_1.scale
|
self.scale: float = self.road_1.scale
|
||||||
self.flinger: Flinger = flinger
|
self.flinger: Flinger = flinger
|
||||||
|
|
||||||
|
@ -697,8 +702,11 @@ class ComplexConnector(Connector):
|
||||||
self.road_1.line.shorten(self.index_1, length)
|
self.road_1.line.shorten(self.index_1, length)
|
||||||
self.road_2.line.shorten(self.index_2, length)
|
self.road_2.line.shorten(self.index_2, length)
|
||||||
|
|
||||||
node: OSMNode = self.road_1.nodes[self.index_1]
|
node_1: OSMNode = self.road_1.nodes[self.index_1]
|
||||||
point: np.ndarray = flinger.fling(node.coordinates)
|
point_1: np.ndarray = flinger.fling(node_1.coordinates)
|
||||||
|
node_2: OSMNode = self.road_2.nodes[self.index_2]
|
||||||
|
point_2: np.ndarray = flinger.fling(node_2.coordinates)
|
||||||
|
point = (point_1 + point_2) / 2
|
||||||
|
|
||||||
points_1: list[np.ndarray] = get_curve_points(
|
points_1: list[np.ndarray] = get_curve_points(
|
||||||
self.road_1,
|
self.road_1,
|
||||||
|
@ -796,15 +804,29 @@ class Roads:
|
||||||
if not self.roads:
|
if not self.roads:
|
||||||
return
|
return
|
||||||
|
|
||||||
layered_roads: dict[float, list[Road]] = {}
|
layered_roads: dict[float, list[Road]] = defaultdict(list)
|
||||||
layered_connectors: dict[float, list[Connector]] = {}
|
layered_connectors: dict[float, list[Connector]] = defaultdict(list)
|
||||||
|
|
||||||
for road in self.roads:
|
for road in self.roads:
|
||||||
if road.layer not in layered_roads:
|
if not road.is_transition:
|
||||||
layered_roads[road.layer] = []
|
|
||||||
layered_roads[road.layer].append(road)
|
layered_roads[road.layer].append(road)
|
||||||
|
else:
|
||||||
|
connections = []
|
||||||
|
for end in 0, -1:
|
||||||
|
connections.append(
|
||||||
|
[
|
||||||
|
connection
|
||||||
|
for connection in self.nodes[road.nodes[end].id_]
|
||||||
|
if not connection[0].is_transition
|
||||||
|
]
|
||||||
|
)
|
||||||
|
if len(connections[0]) == 1 and len(connections[1]) == 1:
|
||||||
|
connector: Connector = ComplexConnector(
|
||||||
|
[connections[0][0], connections[1][0]], flinger
|
||||||
|
)
|
||||||
|
layered_connectors[road.layer].append(connector)
|
||||||
|
|
||||||
for _, connected in self.nodes.items():
|
for connected in self.nodes.values():
|
||||||
connector: Connector
|
connector: Connector
|
||||||
|
|
||||||
if len(connected) <= 1:
|
if len(connected) <= 1:
|
||||||
|
@ -819,19 +841,16 @@ class Roads:
|
||||||
or index_2 not in [0, len(road_2.nodes) - 1]
|
or index_2 not in [0, len(road_2.nodes) - 1]
|
||||||
):
|
):
|
||||||
connector = SimpleConnector(connected, flinger)
|
connector = SimpleConnector(connected, flinger)
|
||||||
else:
|
elif not road_1.is_transition and not road_2.is_transition:
|
||||||
connector = ComplexConnector(connected, flinger)
|
connector = ComplexConnector(connected, flinger)
|
||||||
|
else:
|
||||||
|
continue
|
||||||
else:
|
else:
|
||||||
# We can also use SimpleIntersection(connected, flinger, scale)
|
# We can also use SimpleIntersection(connected, flinger, scale)
|
||||||
# here.
|
# here.
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if connector.min_layer not in layered_connectors:
|
|
||||||
layered_connectors[connector.min_layer] = []
|
|
||||||
layered_connectors[connector.min_layer].append(connector)
|
layered_connectors[connector.min_layer].append(connector)
|
||||||
|
|
||||||
if connector.max_layer not in layered_connectors:
|
|
||||||
layered_connectors[connector.max_layer] = []
|
|
||||||
layered_connectors[connector.max_layer].append(connector)
|
layered_connectors[connector.max_layer].append(connector)
|
||||||
|
|
||||||
for layer in sorted(layered_roads.keys()):
|
for layer in sorted(layered_roads.keys()):
|
||||||
|
@ -844,6 +863,7 @@ class Roads:
|
||||||
|
|
||||||
for road in roads:
|
for road in roads:
|
||||||
road.draw(svg, True)
|
road.draw(svg, True)
|
||||||
|
if connectors:
|
||||||
for connector in connectors:
|
for connector in connectors:
|
||||||
if connector.min_layer == layer:
|
if connector.min_layer == layer:
|
||||||
connector.draw_border(svg)
|
connector.draw_border(svg)
|
||||||
|
@ -852,6 +872,7 @@ class Roads:
|
||||||
|
|
||||||
for road in roads:
|
for road in roads:
|
||||||
road.draw(svg, False)
|
road.draw(svg, False)
|
||||||
|
if connectors:
|
||||||
for connector in connectors:
|
for connector in connectors:
|
||||||
if connector.max_layer == layer:
|
if connector.max_layer == layer:
|
||||||
connector.draw(svg)
|
connector.draw(svg)
|
||||||
|
|
|
@ -24,36 +24,37 @@ SCHEME: Scheme = Scheme(workspace.DEFAULT_SCHEME_PATH)
|
||||||
SHAPE_EXTRACTOR: ShapeExtractor = ShapeExtractor(
|
SHAPE_EXTRACTOR: ShapeExtractor = ShapeExtractor(
|
||||||
workspace.ICONS_PATH, workspace.ICONS_CONFIG_PATH
|
workspace.ICONS_PATH, workspace.ICONS_CONFIG_PATH
|
||||||
)
|
)
|
||||||
|
DEFAULT_ZOOM: float = 18.0
|
||||||
|
|
||||||
|
|
||||||
ROAD_TYPES: list[dict[str, str]] = [
|
HIGHWAY_VALUES: list[str] = [
|
||||||
{"highway": "motorway"},
|
"motorway",
|
||||||
{"highway": "trunk"},
|
"trunk",
|
||||||
{"highway": "primary"},
|
"primary",
|
||||||
{"highway": "secondary"},
|
"secondary",
|
||||||
{"highway": "tertiary"},
|
"tertiary",
|
||||||
{"highway": "unclassified"},
|
"unclassified",
|
||||||
{"highway": "residential"},
|
"residential",
|
||||||
{"highway": "service"},
|
"service",
|
||||||
{"highway": "service_minor"},
|
"service_minor",
|
||||||
{"highway": "road"},
|
"road",
|
||||||
{"highway": "pedestrian"},
|
"pedestrian",
|
||||||
{"highway": "living_street"},
|
"living_street",
|
||||||
{"highway": "bridleway"},
|
"bridleway",
|
||||||
{"highway": "cycleway"},
|
"cycleway",
|
||||||
{"highway": "footway"},
|
"footway",
|
||||||
{"highway": "steps"},
|
"steps",
|
||||||
{"highway": "path"},
|
"path",
|
||||||
{"highway": "track"},
|
"track",
|
||||||
{"highway": "raceway"},
|
"raceway",
|
||||||
]
|
]
|
||||||
|
|
||||||
AEROWAY_TYPES: list[dict[str, str]] = [
|
AEROWAY_VALUES: list[str] = [
|
||||||
{"aeroway": "runway"},
|
"runway",
|
||||||
{"aeroway": "taxiway"},
|
"taxiway",
|
||||||
]
|
]
|
||||||
|
|
||||||
RAILWAY_TYPES: list[dict[str, str]] = [
|
RAILWAY_TAGS: list[dict[str, str]] = [
|
||||||
{"railway": "rail"},
|
{"railway": "rail"},
|
||||||
{"railway": "light_rail"},
|
{"railway": "light_rail"},
|
||||||
{"railway": "monorail"},
|
{"railway": "monorail"},
|
||||||
|
@ -120,9 +121,9 @@ class Grid:
|
||||||
"""Creating map with elements ordered in grid."""
|
"""Creating map with elements ordered in grid."""
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.x_step: float = 0.0003
|
self.x_step: float = 0.0002
|
||||||
self.y_step: float = 0.0003
|
self.y_step: float = 0.0003
|
||||||
self.x_start: float = 0.0028
|
self.x_start: float = 0.0
|
||||||
self.index: int = 0
|
self.index: int = 0
|
||||||
self.nodes: dict[OSMNode, tuple[int, int]] = {}
|
self.nodes: dict[OSMNode, tuple[int, int]] = {}
|
||||||
self.max_j: float = 0
|
self.max_j: float = 0
|
||||||
|
@ -156,7 +157,6 @@ def road_features(
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Draw test image with different road features."""
|
"""Draw test image with different road features."""
|
||||||
osm_data: OSMData = OSMData()
|
osm_data: OSMData = OSMData()
|
||||||
|
|
||||||
grid: Grid = Grid()
|
grid: Grid = Grid()
|
||||||
|
|
||||||
for i, type_ in enumerate(types):
|
for i, type_ in enumerate(types):
|
||||||
|
@ -166,8 +166,8 @@ def road_features(
|
||||||
node: OSMNode = grid.add_node({}, i, j)
|
node: OSMNode = grid.add_node({}, i, j)
|
||||||
|
|
||||||
if previous:
|
if previous:
|
||||||
tags: dict[str, str] = dict(features[j - 1])
|
tags: dict[str, str] = dict(type_)
|
||||||
tags |= type_
|
tags |= dict(features[j - 1])
|
||||||
way: OSMWay = OSMWay(
|
way: OSMWay = OSMWay(
|
||||||
tags, i * (len(features) + 1) + j, [previous, node]
|
tags, i * (len(features) + 1) + j, [previous, node]
|
||||||
)
|
)
|
||||||
|
@ -178,12 +178,15 @@ def road_features(
|
||||||
|
|
||||||
|
|
||||||
def draw(
|
def draw(
|
||||||
osm_data: OSMData, output_path: Path, boundary_box: BoundaryBox
|
osm_data: OSMData,
|
||||||
|
output_path: Path,
|
||||||
|
boundary_box: BoundaryBox,
|
||||||
|
zoom: float = DEFAULT_ZOOM,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Draw map."""
|
"""Draw map."""
|
||||||
configuration: MapConfiguration = MapConfiguration(level="all")
|
configuration: MapConfiguration = MapConfiguration(level="all")
|
||||||
|
|
||||||
flinger: Flinger = Flinger(boundary_box, 18, osm_data.equator_length)
|
flinger: Flinger = Flinger(boundary_box, zoom, osm_data.equator_length)
|
||||||
svg: Drawing = Drawing(output_path.name, flinger.size)
|
svg: Drawing = Drawing(output_path.name, flinger.size)
|
||||||
constructor: Constructor = Constructor(
|
constructor: Constructor = Constructor(
|
||||||
osm_data, flinger, SCHEME, SHAPE_EXTRACTOR, configuration
|
osm_data, flinger, SCHEME, SHAPE_EXTRACTOR, configuration
|
||||||
|
@ -200,16 +203,23 @@ def draw(
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
logging.basicConfig(format="%(levelname)s %(message)s", level=logging.INFO)
|
logging.basicConfig(format="%(levelname)s %(message)s", level=logging.INFO)
|
||||||
|
|
||||||
|
highway_tags: list[dict[str, str]] = [
|
||||||
|
{"highway": value} for value in HIGHWAY_VALUES
|
||||||
|
]
|
||||||
|
aeroway_tags: list[dict[str, str]] = [
|
||||||
|
{"aeroway": value} for value in AEROWAY_VALUES
|
||||||
|
]
|
||||||
|
|
||||||
road_features(
|
road_features(
|
||||||
ROAD_TYPES, ROAD_LANES_AND_FEATURES, Path("out") / "lanes.svg"
|
highway_tags, ROAD_LANES_AND_FEATURES, Path("out") / "lanes.svg"
|
||||||
)
|
)
|
||||||
road_features(
|
road_features(
|
||||||
ROAD_TYPES + RAILWAY_TYPES + AEROWAY_TYPES,
|
highway_tags + RAILWAY_TAGS + aeroway_tags,
|
||||||
ROAD_WIDTHS_AND_FEATURES,
|
ROAD_WIDTHS_AND_FEATURES,
|
||||||
Path("out") / "width.svg",
|
Path("out") / "width.svg",
|
||||||
)
|
)
|
||||||
road_features(
|
road_features(
|
||||||
ROAD_TYPES,
|
highway_tags,
|
||||||
PLACEMENT_FEATURES_1 + [{}] + PLACEMENT_FEATURES_2,
|
PLACEMENT_FEATURES_1 + [{"highway": "none"}] + PLACEMENT_FEATURES_2,
|
||||||
Path("out") / "placement.svg",
|
Path("out") / "placement.svg",
|
||||||
)
|
)
|
||||||
|
|
Loading…
Add table
Reference in a new issue