diff --git a/doc/buildings.png b/doc/buildings.png
index 2e71aa8..5ff9392 100644
Binary files a/doc/buildings.png and b/doc/buildings.png differ
diff --git a/doc/grid.png b/doc/grid.png
index f124a02..4c5f506 100644
Binary files a/doc/grid.png and b/doc/grid.png differ
diff --git a/doc/time.png b/doc/time.png
index 5663c39..7843196 100644
Binary files a/doc/time.png and b/doc/time.png differ
diff --git a/doc/trees.png b/doc/trees.png
index 86d53c5..86f1913 100644
Binary files a/doc/trees.png and b/doc/trees.png differ
diff --git a/doc/user.png b/doc/user.png
index 2bafdaa..0f7f4c5 100644
Binary files a/doc/user.png and b/doc/user.png differ
diff --git a/doc/viewpoints.png b/doc/viewpoints.png
index 20bad06..f4ee8ea 100644
Binary files a/doc/viewpoints.png and b/doc/viewpoints.png differ
diff --git a/icons/icons.svg b/icons/icons.svg
index a120055..5b66231 100644
--- a/icons/icons.svg
+++ b/icons/icons.svg
@@ -145,12 +145,12 @@
inkscape:object-paths="true"
inkscape:guide-bbox="true"
showguides="false"
- showgrid="false"
+ showgrid="true"
inkscape:document-units="px"
inkscape:current-layer="layer1"
- inkscape:cy="234.45318"
- inkscape:cx="89.302491"
- inkscape:zoom="11.313708"
+ inkscape:cy="242.47473"
+ inkscape:cx="149.51068"
+ inkscape:zoom="16"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
borderopacity="1.0"
@@ -3597,7 +3597,7 @@
sodipodi:nodetypes="ccccccc"
inkscape:connector-curvature="0"
id="electricity"
- d="m 73,177 -5,8 5,0 -2,6 5,-8 -5,0 z"
+ d="m 73,178 -5,7 4,0 -1,5 5,-7 -4,0 z"
style="fill:#000000;stroke:none" />
-
-
-
+ id="u_bahn"
+ inkscape:label="#rect5457" />
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/roentgen/constructor.py b/roentgen/constructor.py
index b59b3f1..f44f98d 100644
--- a/roentgen/constructor.py
+++ b/roentgen/constructor.py
@@ -32,8 +32,8 @@ def is_clockwise(polygon: List[OSMNode]) -> bool:
for index in range(len(polygon)): # type: int
next_index: int = 0 if index == len(polygon) - 1 else index + 1
count += (
- (polygon[next_index].position[0] - polygon[index].position[0]) *
- (polygon[next_index].position[1] + polygon[index].position[1]))
+ (polygon[next_index].coordinates[0] - polygon[index].coordinates[0]) *
+ (polygon[next_index].coordinates[1] + polygon[index].coordinates[1]))
return count >= 0
@@ -116,17 +116,43 @@ class Figure(Tagged):
return path
+class Segment:
+ def __init__(self, point_1: np.array, point_2: np.array):
+ self.point_1 = point_1
+ self.point_2 = point_2
+
+ difference: np.array = point_2 - point_1
+ vector: np.array = difference / np.linalg.norm(difference)
+ self.angle: float = (
+ np.arccos(np.dot(vector, np.array((0, 1)))) / np.pi)
+
+ def __lt__(self, other: "Segment"):
+ return (((self.point_1 + self.point_2) / 2)[1] <
+ ((other.point_1 + other.point_2) / 2)[1])
+
+
class Building(Figure):
def __init__(
- self, tags: Dict[str, str], inners, outers, style: Dict[str, Any],
- layer: float):
+ self, tags: Dict[str, str], inners, outers, flinger: Flinger,
+ style: Dict[str, Any], layer: float):
super().__init__(tags, inners, outers, style, layer)
+ self.parts = []
+
+ for nodes in self.inners + self.outers:
+ for i in range(len(nodes) - 1):
+ flung_1: np.array = flinger.fling(nodes[i].coordinates)
+ flung_2: np.array = flinger.fling(nodes[i + 1].coordinates)
+ self.parts.append(Segment(flung_1, flung_2))
+
+ self.parts = sorted(self.parts)
+
+
def get_levels(self):
try:
- return float(self.get_tag("building:levels"))
+ return max(3, float(self.get_tag("building:levels")))
except (ValueError, TypeError):
- return 1
+ return 3
class TextStruct:
@@ -147,8 +173,8 @@ def line_center(nodes: List[OSMNode], flinger: Flinger) -> np.array:
boundary = [MinMax(), MinMax()]
for node in nodes: # type: OSMNode
- boundary[0].update(node.position[0])
- boundary[1].update(node.position[1])
+ boundary[0].update(node.coordinates[0])
+ boundary[1].update(node.coordinates[1])
center_coordinates = np.array((boundary[0].center(), boundary[1].center()))
return flinger.fling(center_coordinates), center_coordinates
@@ -213,7 +239,7 @@ def get_path(nodes: List[OSMNode], shift: np.array, flinger: Flinger) -> str:
path = ""
prev_node = None
for node in nodes:
- flung = flinger.fling(node.position) + shift
+ flung = flinger.fling(node.coordinates) + shift
path += ("L" if prev_node else "M") + f" {flung[0]},{flung[1]} "
prev_node = node
if nodes[0] == nodes[-1]:
@@ -242,6 +268,12 @@ class Constructor:
self.figures: List[Figure] = []
self.buildings: List[Figure] = []
+ self.levels: Set[float] = {0.5, 1}
+
+ def add_building(self, building: Building):
+ self.buildings.append(building)
+ self.levels.add(building.get_levels())
+
def construct_ways(self):
"""
Construct Röntgen ways.
@@ -355,8 +387,8 @@ class Constructor:
element["r2"] * \
self.flinger.get_scale(center_coordinates) + 2
if "building" in tags:
- self.buildings.append(
- Building(tags, inners, outers, style, layer))
+ self.add_building(Building(
+ tags, inners, outers, self.flinger, style, layer))
else:
self.figures.append(
Figure(tags, inners, outers, style, layer))
@@ -414,7 +446,7 @@ class Constructor:
s = sorted(
self.map_.node_map.keys(),
- key=lambda x: -self.map_.node_map[x].position[0])
+ key=lambda x: -self.map_.node_map[x].coordinates[0])
for node_id in s: # type: int
node_number += 1
@@ -422,7 +454,7 @@ class Constructor:
node_number, len(self.map_.node_map),
text="Constructing nodes")
node: OSMNode = self.map_.node_map[node_id]
- flung = self.flinger.fling(node.position)
+ flung = self.flinger.fling(node.coordinates)
tags = node.tags
if not self.check_level(tags):
@@ -440,6 +472,6 @@ class Constructor:
if self.mode == "time":
icon_set.color = get_time_color(node.timestamp)
- self.nodes.append(Point(icon_set, tags, flung, node.position))
+ self.nodes.append(Point(icon_set, tags, flung, node.coordinates))
ui.progress_bar(-1, len(self.map_.node_map), text="Constructing nodes")
diff --git a/roentgen/mapper.py b/roentgen/mapper.py
index 025d32f..b8aa5b5 100644
--- a/roentgen/mapper.py
+++ b/roentgen/mapper.py
@@ -225,32 +225,6 @@ class Painter:
texts.append(TextStruct(tags[tag]))
return texts
- def draw_building_walls(self, stage, color: Color, ways):
- """
- Draw area between way and way shifted by the vector.
- """
- for way in ways: # type: Building
- if stage == 1:
- shift_1 = [0, 0]
- shift_2 = [0, -1]
- elif stage == 2:
- shift_1 = [0, -1]
- shift_2 = [0, -2]
- else:
- shift_1 = [0, -2]
- shift_2 = [0, min(-3, -1 * way.get_levels())]
-
- for nodes in way.inners + way.outers:
- for i in range(len(nodes) - 1):
- flung_1 = self.flinger.fling(nodes[i].position)
- flung_2 = self.flinger.fling(nodes[i + 1].position)
-
- self.svg.add(self.svg.path(
- d=("M", np.add(flung_1, shift_1), "L",
- np.add(flung_2, shift_1), np.add(flung_2, shift_2),
- np.add(flung_1, shift_2), "Z"),
- fill=color.hex, stroke="#CCCCCC", stroke_width=1))
-
def draw(self, constructor: Constructor, points):
"""
Draw map.
@@ -271,12 +245,11 @@ class Painter:
building_shade = Group(opacity=0.1)
for way in constructor.buildings: # type: Building
- shift = [-5, 5]
- shift = [-5 * way.get_levels(), 5 * way.get_levels()]
+ shift = [2 * way.get_levels(), 0 * way.get_levels()]
for nodes11 in way.inners + way.outers:
for i in range(len(nodes11) - 1):
- flung_1 = self.flinger.fling(nodes11[i].position)
- flung_2 = self.flinger.fling(nodes11[i + 1].position)
+ flung_1 = self.flinger.fling(nodes11[i].coordinates)
+ flung_2 = self.flinger.fling(nodes11[i + 1].coordinates)
building_shade.add(Path(
("M", flung_1, "L", flung_2, np.add(flung_2, shift),
np.add(flung_1, shift), "Z"),
@@ -284,26 +257,46 @@ class Painter:
self.svg.add(building_shade)
- # Building walls
+ previous_level: float = 0
- self.draw_building_walls(1, Color("#AAAAAA"), constructor.buildings)
- self.draw_building_walls(2, Color("#C3C3C3"), constructor.buildings)
- self.draw_building_walls(3, Color("#DDDDDD"), constructor.buildings)
+ height = 1
- # Building roof
+ for level in sorted(constructor.levels):
+ fill: Color()
+ for way in constructor.buildings: # type: Building
+ if way.get_levels() < level:
+ continue
+ shift_1 = [0, -previous_level * height]
+ shift_2 = [0, -level * height]
+ for segment in way.parts:
+ if level == 0.5:
+ fill = Color("#AAAAAA")
+ elif level == 1:
+ fill = Color("#C3C3C3")
+ else:
+ a = 0.8 + segment.angle * 0.2
+ fill = Color(rgb=(a, a, a))
- def sort_by_levels(building: Building):
- return building.get_levels()
+ self.svg.add(self.svg.path(
+ d=("M", np.add(segment.point_1, shift_1), "L",
+ np.add(segment.point_2, shift_1),
+ np.add(segment.point_2, shift_2),
+ np.add(segment.point_1, shift_2),
+ np.add(segment.point_1, shift_1), "Z"),
+ fill=fill.hex, stroke=fill.hex, stroke_width=1,
+ stroke_linejoin="round"))
- for way in sorted(constructor.buildings, key=sort_by_levels): # type: Building
- shift = [0, -3]
- shift = np.array([
- 0 * way.get_levels(), min(-3, -1 * way.get_levels())])
- path: str = way.get_path(self.flinger, shift)
- if path:
- p = Path(d=path, opacity=1)
- p.update(way.style)
- self.svg.add(p)
+ # Draw roof.
+
+ if way.get_levels() == level:
+ shift = np.array([0, -way.get_levels() * height])
+ path: str = way.get_path(self.flinger, shift)
+ p = Path(d=path, opacity=1)
+ p.update(way.style)
+ p.update({"stroke-linejoin": "round"})
+ self.svg.add(p)
+
+ previous_level = level
# Trees
diff --git a/roentgen/osm_reader.py b/roentgen/osm_reader.py
index 1af1120..926f2cf 100644
--- a/roentgen/osm_reader.py
+++ b/roentgen/osm_reader.py
@@ -42,7 +42,7 @@ class OSMNode(Tagged):
super().__init__()
self.id_: Optional[int] = None
- self.position: Optional[np.array] = None
+ self.coordinates: Optional[np.array] = None
self.visible: Optional[str] = None
self.changeset: Optional[str] = None
@@ -58,7 +58,7 @@ class OSMNode(Tagged):
:param is_full: if false, parse only ID, latitude and longitude
"""
self.id_ = int(get_value("id", text))
- self.position = np.array((
+ self.coordinates = np.array((
float(get_value("lat", text)), float(get_value("lon", text))))
if is_full: