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: