diff --git a/roentgen/constructor.py b/roentgen/constructor.py index 99e0f62..d96e4f4 100644 --- a/roentgen/constructor.py +++ b/roentgen/constructor.py @@ -4,7 +4,6 @@ Construct Röntgen nodes and ways. Author: Sergey Vartanov (me@enzet.ru). """ from collections import Counter -from dataclasses import dataclass from datetime import datetime from hashlib import sha256 from typing import Any, Dict, List, Optional, Set @@ -14,14 +13,15 @@ import numpy as np from roentgen import ui from roentgen.color import get_gradient_color -from roentgen.icon import DEFAULT_SMALL_SHAPE_ID +from roentgen.icon import DEFAULT_SMALL_SHAPE_ID, IconExtractor from roentgen.flinger import Flinger from roentgen.osm_reader import ( Map, OSMMember, OSMRelation, OSMWay, OSMNode, Tagged) +from roentgen.point import Point from roentgen.scheme import IconSet, Scheme, LineStyle from roentgen.util import MinMax -DEBUG: bool = True +DEBUG: bool = False TIME_COLOR_SCALE: List[Color] = [ Color("#581845"), Color("#900C3F"), Color("#C70039"), Color("#FF5733"), Color("#FFC300"), Color("#DAF7A6")] @@ -60,27 +60,6 @@ def make_counter_clockwise(polygon: List[OSMNode]) -> List[OSMNode]: return polygon if not is_clockwise(polygon) else list(reversed(polygon)) -class Point(Tagged): - """ - Object on the map with no dimensional attributes. - """ - def __init__( - self, icon_set: IconSet, tags: Dict[str, str], point: np.array, - coordinates: np.array, priority: float = 0, - is_for_node: bool = True): - super().__init__() - - assert point is not None - - self.icon_set: IconSet = icon_set - self.tags: Dict[str, str] = tags - self.point: np.array = point - self.coordinates: np.array = coordinates - self.priority: float = priority - self.layer: float = 0 - self.is_for_node: bool = is_for_node - - class Figure(Tagged): """ Some figure on the map: way or area. @@ -168,16 +147,6 @@ class Building(Figure): return 3 -@dataclass -class TextStruct: - """ - Some label on the map with attributes. - """ - text: str - fill: Color = Color("#444444") - size: float = 10 - - def line_center(nodes: List[OSMNode], flinger: Flinger) -> np.array: """ Get geometric center of nodes set. @@ -280,7 +249,7 @@ class Constructor: """ def __init__( self, check_level, mode: str, seed: str, map_: Map, - flinger: Flinger, scheme: Scheme): + flinger: Flinger, scheme: Scheme, icon_extractor: IconExtractor): self.check_level = check_level self.mode: str = mode @@ -288,6 +257,7 @@ class Constructor: self.map_: Map = map_ self.flinger: Flinger = flinger self.scheme: Scheme = scheme + self.icon_extractor = icon_extractor self.nodes: List[Point] = [] self.figures: List[Figure] = [] @@ -331,8 +301,6 @@ class Constructor: """ assert len(outers) >= 1 - line_is_cycle: bool = is_cycle(outers[0]) - center_point, center_coordinates = ( line_center(outers[0], self.flinger)) @@ -368,7 +336,8 @@ class Constructor: else: self.figures.append( Figure(line.tags, inners, outers, line_style)) - icon_set: IconSet = self.scheme.get_icon(line.tags, for_="line") + icon_set: IconSet = self.scheme.get_icon( + self.icon_extractor, line.tags, for_="line") self.nodes.append(Point( icon_set, line.tags, center_point, center_coordinates, is_for_node=False)) @@ -380,7 +349,8 @@ class Constructor: "stroke-width": 1} self.figures.append(Figure( line.tags, inners, outers, LineStyle(style, 1000))) - icon_set: IconSet = self.scheme.get_icon(line.tags) + icon_set: IconSet = self.scheme.get_icon( + self.icon_extractor, line.tags) self.nodes.append(Point( icon_set, line.tags, center_point, center_coordinates, is_for_node=False)) @@ -433,7 +403,7 @@ class Constructor: if not self.check_level(tags): continue - icon_set: IconSet = self.scheme.get_icon(tags) + icon_set: IconSet = self.scheme.get_icon(self.icon_extractor, tags) if self.mode in ["time", "user-coloring"]: if not tags: @@ -450,7 +420,4 @@ class Constructor: f"{key}: {tags[key]}" for key in tags if key not in icon_set.processed) - for t in missing_tags.most_common(): - print(t) - ui.progress_bar(-1, len(self.map_.node_map), text="Constructing nodes") diff --git a/roentgen/icon.py b/roentgen/icon.py index 927cd05..c81113a 100644 --- a/roentgen/icon.py +++ b/roentgen/icon.py @@ -6,10 +6,12 @@ Author: Sergey Vartanov (me@enzet.ru). import re import xml.dom.minidom from dataclasses import dataclass -from typing import Dict +from typing import Dict, Any from xml.dom.minidom import Document, Element, Node import numpy as np +import svgwrite +from colour import Color from svgwrite import Drawing from roentgen import ui @@ -49,6 +51,36 @@ class Icon: return svg.path( d=self.path, transform=f"translate({shift[0]},{shift[1]})") + def draw( + self, svg: svgwrite.Drawing, point: np.array, color: Color, + opacity=1.0, tags: Dict[str, Any] = None, outline: bool = False): + """ + Draw icon shape into SVG file. + + :param svg: output SVG file + :param point: icon position + :param color: fill color + :param opacity: icon opacity + :param tags: tags to be displayed as hint + :param outline: draw outline for the icon + """ + point = np.array(list(map(int, point))) + + path: svgwrite.path.Path = self.get_path(svg, point) + path.update({"fill": color.hex}) + if outline: + opacity: float = 0.5 + + path.update({ + "fill": color.hex, "stroke": color.hex, "stroke-width": 2.2, + "stroke-linejoin": "round"}) + if opacity != 1.0: + path.update({"opacity": opacity}) + if tags: + title: str = "\n".join(map(lambda x: x + ": " + tags[x], tags)) + path.set_desc(title=title) + svg.add(path) + class IconExtractor: """ diff --git a/roentgen/mapper.py b/roentgen/mapper.py index f4befd8..fc7d863 100644 --- a/roentgen/mapper.py +++ b/roentgen/mapper.py @@ -14,13 +14,12 @@ from colour import Color from svgwrite.container import Group from svgwrite.path import Path from svgwrite.shapes import Rect -from svgwrite.text import Text -from typing import Any, Dict, List, Optional +from typing import Any, Dict from roentgen import ui -from roentgen.address import get_address from roentgen.constructor import ( - Constructor, Point, Figure, TextStruct, Building, Segment) + Constructor, Figure, Building, Segment) +from roentgen.point import Point from roentgen.flinger import Flinger from roentgen.grid import draw_grid from roentgen.icon import Icon, IconExtractor @@ -29,7 +28,6 @@ from roentgen.osm_reader import Map, OSMReader from roentgen.scheme import Scheme from roentgen.direction import DirectionSet, Sector from roentgen.util import MinMax -from roentgen.color import is_bright ICONS_FILE_NAME: str = "icons/icons.svg" TAGS_FILE_NAME: str = "data/tags.yml" @@ -38,8 +36,6 @@ MISSING_TAGS_FILE_NAME: str = "missing_tags.yml" AUTHOR_MODE = "user-coloring" CREATION_TIME_MODE = "time" -DEFAULT_FONT = "Roboto" - class Painter: """ @@ -64,160 +60,6 @@ class Painter: self.icon_extractor = icon_extractor self.scheme: Scheme = scheme - def draw_shapes(self, node: Point, points: List[List[float]]): - """ - Draw shapes for one node. - """ - if node.icon_set.is_default and not node.is_for_node: - return - - left: float = -(len(node.icon_set.icons) - 1) * 8 - - if self.overlap != 0: - for shape_ids in node.icon_set.icons: - has_space = True - for p in points[-1000:]: - if node.point[0] + left - self.overlap <= p[0] \ - <= node.point[0] + left + self.overlap and \ - node.point[1] - self.overlap <= p[1] \ - <= node.point[1] + self.overlap: - has_space = False - break - if has_space: - self.draw_point_shape( - shape_ids, (node.point[0] + left, node.point[1]), - node.icon_set.color, tags=node.tags) - points.append([node.point[0] + left, node.point[1]]) - left += 16 - else: - for shape_ids in node.icon_set.icons: - self.draw_point_shape( - shape_ids, (node.point[0] + left, node.point[1]), - node.icon_set.color, tags=node.tags) - left += 16 - - def draw_texts(self, node: Point): - """ - Draw all labels. - """ - text_y: float = 0 - - write_tags = self.construct_text(node.tags, node.icon_set.processed) - - for text_struct in write_tags: # type: TextStruct - text_y += text_struct.size + 1 - text = text_struct.text - text = text.replace(""", '"') - text = text.replace("&", '&') - text = text[:26] + ("..." if len(text) > 26 else "") - self.draw_text( - text, (node.point[0], node.point[1] + text_y + 8), - text_struct.fill, size=text_struct.size) - - def draw_text( - self, text: str, point, fill: Color, size: float = 10, - out_fill=Color("white"), out_opacity=1.0, - out_fill_2: Optional[Color] = None, out_opacity_2=1.0): - """ - Drawing text. - - ###### ### outline 2 - #------# --- outline 1 - #| Text |# - #------# - ###### - """ - if out_fill_2: - self.svg.add(Text( - text, point, font_size=size, text_anchor="middle", - font_family=DEFAULT_FONT, fill=out_fill_2.hex, - stroke_linejoin="round", stroke_width=5, - stroke=out_fill_2.hex, opacity=out_opacity_2)) - if out_fill: - self.svg.add(Text( - text, point, font_size=size, text_anchor="middle", - font_family=DEFAULT_FONT, fill=out_fill.hex, - stroke_linejoin="round", stroke_width=3, - stroke=out_fill.hex, opacity=out_opacity)) - self.svg.add(Text( - text, point, font_size=size, text_anchor="middle", - font_family=DEFAULT_FONT, fill=fill.hex)) - - def construct_text(self, tags, processed) -> List[TextStruct]: - """ - Construct labels for not processed tags. - """ - texts: List[TextStruct] = [] - - name = None - alt_name = None - if "name" in tags: - name = tags["name"] - tags.pop("name", None) - if "name:ru" in tags: - if not name: - name = tags["name:ru"] - tags.pop("name:ru", None) - tags.pop("name:ru", None) - if "name:en" in tags: - if not name: - name = tags["name:en"] - tags.pop("name:en", None) - tags.pop("name:en", None) - if "alt_name" in tags: - if alt_name: - alt_name += ", " - else: - alt_name = "" - alt_name += tags["alt_name"] - tags.pop("alt_name") - if "old_name" in tags: - if alt_name: - alt_name += ", " - else: - alt_name = "" - alt_name += "бывш. " + tags["old_name"] - - address: List[str] = get_address(tags, self.draw_captions) - - if name: - texts.append(TextStruct(name, Color("black"))) - if alt_name: - texts.append(TextStruct("(" + alt_name + ")")) - if address: - texts.append(TextStruct(", ".join(address))) - - if self.draw_captions == "main": - return texts - - if "route_ref" in tags: - texts.append(TextStruct(tags["route_ref"].replace(";", " "))) - tags.pop("route_ref", None) - if "cladr:code" in tags: - texts.append(TextStruct(tags["cladr:code"], size=7)) - tags.pop("cladr:code", None) - if "website" in tags: - link = tags["website"] - if link[:7] == "http://": - link = link[7:] - if link[:8] == "https://": - link = link[8:] - if link[:4] == "www.": - link = link[4:] - if link[-1] == "/": - link = link[:-1] - link = link[:25] + ("..." if len(tags["website"]) > 25 else "") - texts.append(TextStruct(link, Color("#000088"))) - tags.pop("website", None) - for k in ["phone"]: - if k in tags: - texts.append(TextStruct(tags[k], Color("#444444"))) - tags.pop(k) - for tag in tags: - if self.scheme.is_writable(tag) and not (tag in processed): - texts.append(TextStruct(tags[tag])) - return texts - def draw(self, constructor: Constructor, points): """ Draw map. @@ -332,21 +174,21 @@ class Painter: angle = float(node.get_tag("camera:angle")) if "angle" in node.tags: angle = float(node.get_tag("angle")) - direction_radius: float = \ - 25 * self.flinger.get_scale(node.coordinates) - direction_color: Color = \ - self.scheme.get_color("direction_camera_color") + direction_radius: float = ( + 25 * self.flinger.get_scale(node.coordinates)) + direction_color: Color = ( + self.scheme.get_color("direction_camera_color")) elif node.get_tag("traffic_sign") == "stop": direction = node.get_tag("direction") - direction_radius: float = \ - 25 * self.flinger.get_scale(node.coordinates) + direction_radius: float = ( + 25 * self.flinger.get_scale(node.coordinates)) direction_color: Color = Color("red") else: direction = node.get_tag("direction") - direction_radius: float = \ - 50 * self.flinger.get_scale(node.coordinates) - direction_color: Color = \ - self.scheme.get_color("direction_view_color") + direction_radius: float = ( + 50 * self.flinger.get_scale(node.coordinates)) + direction_color: Color = ( + self.scheme.get_color("direction_view_color")) is_revert_gradient = True if not direction: @@ -375,16 +217,16 @@ class Painter: d=["M", point] + path + ["L", point, "Z"], fill=gradient.get_paint_server())) - # All other nodes + # All other points nodes = sorted(constructor.nodes, key=lambda x: x.layer) for index, node in enumerate(nodes): # type: int, Point - if node.get_tag("natural") == "tree" and \ + if (node.get_tag("natural") == "tree" and ("diameter_crown" in node.tags or - "circumference" in node.tags): + "circumference" in node.tags)): continue ui.progress_bar(index, len(nodes), step=10, text="Drawing nodes") - self.draw_shapes(node, points) + node.draw_shapes(self.svg) ui.progress_bar(-1, len(nodes), step=10, text="Drawing nodes") if self.draw_captions == "no": @@ -392,52 +234,7 @@ class Painter: for node in nodes: # type: Point if self.mode not in [CREATION_TIME_MODE, AUTHOR_MODE]: - self.draw_texts(node) - - def draw_point_shape( - self, shape_ids: List[str], point, fill: Color, tags=None): - """ - Draw one icon. - """ - if self.mode not in [CREATION_TIME_MODE, AUTHOR_MODE]: - for shape_id in shape_ids: # type: str - icon, _ = self.icon_extractor.get_path(shape_id) - self.draw_point_outline(icon, point, fill, mode=self.mode) - for shape_id in shape_ids: # type: str - icon, _ = self.icon_extractor.get_path(shape_id) - self.draw_point(icon, point, fill, tags=tags) - - def draw_point( - self, icon: Icon, point: (float, float), fill: Color, - tags: Dict[str, str] = None) -> None: - - point = np.array(list(map(int, point))) - title: str = "\n".join(map(lambda x: x + ": " + tags[x], tags)) - - path: svgwrite.path.Path = icon.get_path(self.svg, point) - path.update({"fill": fill.hex}) - path.set_desc(title=title) - self.svg.add(path) - - def draw_point_outline( - self, icon: Icon, point, fill: Color, mode="default"): - - point = np.array(list(map(int, point))) - - opacity: float = 0.5 - stroke_width: float = 2.2 - outline_fill: Color = self.scheme.get_color("outline_color") - - if mode not in [AUTHOR_MODE, CREATION_TIME_MODE] and is_bright(fill): - outline_fill = Color("black") - opacity = 0.7 - - path = icon.get_path(self.svg, point) - path.update({ - "fill": outline_fill.hex, "opacity": opacity, - "stroke": outline_fill.hex, "stroke-width": stroke_width, - "stroke-linejoin": "round"}) - self.svg.add(path) + node.draw_texts(self.svg, self.scheme, self.draw_captions) def check_level_number(tags: Dict[str, Any], level: float): @@ -479,6 +276,11 @@ def check_level_overground(tags: Dict[str, Any]) -> bool: def main(argv) -> None: + """ + Röntgen entry point. + + :param argv: command-line arguments + """ if len(argv) == 2: if argv[1] == "grid": draw_grid() @@ -532,8 +334,8 @@ def main(argv) -> None: flinger: Flinger = Flinger(MinMax(min1, max1), options.scale) size: np.array = flinger.size - svg: svgwrite.Drawing = \ - svgwrite.Drawing(options.output_file_name, size=size) + svg: svgwrite.Drawing = ( + svgwrite.Drawing(options.output_file_name, size=size)) svg.add(Rect((0, 0), size, fill=background_color)) icon_extractor: IconExtractor = IconExtractor(ICONS_FILE_NAME) @@ -555,7 +357,8 @@ def main(argv) -> None: return not check_level_number(x, float(options.level)) constructor: Constructor = Constructor( - check_level, options.mode, options.seed, map_, flinger, scheme) + check_level, options.mode, options.seed, map_, flinger, scheme, + icon_extractor) if options.draw_ways: constructor.construct_ways() constructor.construct_relations() diff --git a/roentgen/point.py b/roentgen/point.py new file mode 100644 index 0000000..8420795 --- /dev/null +++ b/roentgen/point.py @@ -0,0 +1,219 @@ +from dataclasses import dataclass +from typing import Dict, Optional, List + +import numpy as np +import svgwrite +from colour import Color + +from roentgen.address import get_address +from roentgen.color import is_bright +from roentgen.icon import Icon +from roentgen.osm_reader import Tagged +from roentgen.scheme import IconSet + +DEFAULT_FONT: str = "Roboto" +DEFAULT_COLOR: Color = Color("#444444") + + +@dataclass +class TextStruct: + """ + Some label on the map with attributes. + """ + text: str + fill: Color = DEFAULT_COLOR + size: float = 10.0 + + +def draw_point_shape( + svg: svgwrite.Drawing, icons: List[Icon], point, fill: Color, + tags=None): + """ + Draw one combined icon and its outline. + """ + # Down-cast floats to integers to make icons pixel-perfect. + point = np.array(list(map(int, point))) + + # Draw outlines. + + for icon in icons: # type: Icon + bright: bool = is_bright(fill) + color: Color = Color("black") if bright else Color("white") + opacity: float = 0.7 if bright else 0.5 + icon.draw(svg, point, color, opacity=opacity, outline=True) + + # Draw icons. + + for icon in icons: # type: Icon + icon.draw(svg, point, fill, tags=tags) + + +def draw_text( + svg: svgwrite.Drawing, text: str, point, fill: Color, + size: float = 10.0, out_fill=Color("white"), out_opacity=1.0, + out_fill_2: Optional[Color] = None, out_opacity_2=1.0): + """ + Drawing text. + + ###### ### outline 2 + #------# --- outline 1 + #| Text |# + #------# + ###### + """ + if out_fill_2: + svg.add(svg.text( + text, point, font_size=size, text_anchor="middle", + font_family=DEFAULT_FONT, fill=out_fill_2.hex, + stroke_linejoin="round", stroke_width=5, + stroke=out_fill_2.hex, opacity=out_opacity_2)) + if out_fill: + svg.add(svg.text( + text, point, font_size=size, text_anchor="middle", + font_family=DEFAULT_FONT, fill=out_fill.hex, + stroke_linejoin="round", stroke_width=3, + stroke=out_fill.hex, opacity=out_opacity)) + svg.add(svg.text( + text, point, font_size=size, text_anchor="middle", + font_family=DEFAULT_FONT, fill=fill.hex)) + + +def construct_text( + tags, processed, scheme, draw_captions) -> List["TextStruct"]: + """ + Construct labels for not processed tags. + """ + texts: List[TextStruct] = [] + + name = None + alt_name = None + if "name" in tags: + name = tags["name"] + tags.pop("name", None) + if "name:ru" in tags: + if not name: + name = tags["name:ru"] + tags.pop("name:ru", None) + tags.pop("name:ru", None) + if "name:en" in tags: + if not name: + name = tags["name:en"] + tags.pop("name:en", None) + tags.pop("name:en", None) + if "alt_name" in tags: + if alt_name: + alt_name += ", " + else: + alt_name = "" + alt_name += tags["alt_name"] + tags.pop("alt_name") + if "old_name" in tags: + if alt_name: + alt_name += ", " + else: + alt_name = "" + alt_name += "бывш. " + tags["old_name"] + + address: List[str] = get_address(tags, draw_captions) + + if name: + texts.append(TextStruct(name, Color("black"))) + if alt_name: + texts.append(TextStruct(f"({alt_name})")) + if address: + texts.append(TextStruct(", ".join(address))) + + if draw_captions == "main": + return texts + + if "route_ref" in tags: + texts.append(TextStruct(tags["route_ref"].replace(";", " "))) + tags.pop("route_ref", None) + if "cladr:code" in tags: + texts.append(TextStruct(tags["cladr:code"], size=7)) + tags.pop("cladr:code", None) + if "website" in tags: + link = tags["website"] + if link[:7] == "http://": + link = link[7:] + if link[:8] == "https://": + link = link[8:] + if link[:4] == "www.": + link = link[4:] + if link[-1] == "/": + link = link[:-1] + link = link[:25] + ("..." if len(tags["website"]) > 25 else "") + texts.append(TextStruct(link, Color("#000088"))) + tags.pop("website", None) + for k in ["phone"]: + if k in tags: + texts.append(TextStruct(tags[k], Color("#444444"))) + tags.pop(k) + for tag in tags: + if scheme.is_writable(tag) and not (tag in processed): + texts.append(TextStruct(tags[tag])) + return texts + + +class Point(Tagged): + """ + Object on the map with no dimensional attributes. + + It may have icons and text. + """ + def __init__( + self, icon_set: IconSet, tags: Dict[str, str], point: np.array, + coordinates: np.array, priority: float = 0, + is_for_node: bool = True): + super().__init__() + + assert point is not None + + self.icon_set: IconSet = icon_set + self.tags: Dict[str, str] = tags + self.point: np.array = point + self.coordinates: np.array = coordinates + self.priority: float = priority + self.layer: float = 0 + self.is_for_node: bool = is_for_node + + self.y = 0 + + def draw_shapes(self, svg: svgwrite.Drawing): + """ + Draw shapes for one node. + """ + if self.icon_set.main_icon and (not self.icon_set.main_icon[0].is_default() or self.is_for_node): + draw_point_shape( + svg, self.icon_set.main_icon, + self.point + np.array((0, self.y)), self.icon_set.color, + tags=self.tags) + self.y += 16 + + left: float = -(len(self.icon_set.extra_icons) - 1) * 8 + + for shape_ids in self.icon_set.extra_icons: + draw_point_shape( + svg, shape_ids, self.point + np.array((left, self.y)), + Color("#888888")) + left += 16 + + if self.icon_set.extra_icons: + self.y += 16 + + def draw_texts(self, svg: svgwrite.Drawing, scheme, draw_captions): + """ + Draw all labels. + """ + write_tags = construct_text( + self.tags, self.icon_set.processed, scheme, draw_captions) + + for text_struct in write_tags: # type: TextStruct + self.y += text_struct.size + 1 + text = text_struct.text + text = text.replace(""", '"') + text = text.replace("&", '&') + text = text[:26] + ("..." if len(text) > 26 else "") + draw_text( + svg, text, self.point + np.array((0, self.y - 8)), + text_struct.fill, size=text_struct.size) diff --git a/roentgen/scheme.py b/roentgen/scheme.py index 0a74923..746e204 100644 --- a/roentgen/scheme.py +++ b/roentgen/scheme.py @@ -10,7 +10,7 @@ from colour import Color from dataclasses import dataclass from typing import Any, Dict, List, Optional, Set, Union -from roentgen.icon import DEFAULT_SHAPE_ID +from roentgen.icon import DEFAULT_SHAPE_ID, IconExtractor, Icon DEFAULT_COLOR: Color = Color("#444444") @@ -20,7 +20,8 @@ class IconSet: """ Node representation: icons and color. """ - icons: List[List[str]] # list of lists of shape identifiers + main_icon: List[Icon] # list of icons + extra_icons: List[List[Icon]] # list of lists of icons color: Color # fill color of all icons # tag keys that were processed to create icon set (other # tag keys should be displayed by text or ignored) @@ -111,7 +112,9 @@ class Scheme: return True return False - def get_icon(self, tags: Dict[str, Any], for_: str = "node") -> IconSet: + def get_icon( + self, icon_extractor: IconExtractor, tags: Dict[str, Any], + for_: str = "node") -> IconSet: """ Construct icon set. @@ -123,8 +126,8 @@ class Scheme: if tags_hash in self.cache: return self.cache[tags_hash] - main_icon: Optional[List[str]] = None - extra_icons: List[List[str]] = [] + main_icon_id: Optional[List[str]] = None + extra_icon_ids: List[List[str]] = [] processed: Set[str] = set() fill: Color = DEFAULT_COLOR @@ -150,15 +153,15 @@ class Scheme: if "draw" in matcher and not matcher["draw"]: processed |= set(matcher["tags"].keys()) if "icon" in matcher: - main_icon = copy.deepcopy(matcher["icon"]) + main_icon_id = copy.deepcopy(matcher["icon"]) processed |= set(matcher["tags"].keys()) if "over_icon" in matcher: - if main_icon: # TODO: check main icon in under icons - main_icon += matcher["over_icon"] + if main_icon_id: # TODO: check main icon in under icons + main_icon_id += matcher["over_icon"] for key in matcher["tags"].keys(): processed.add(key) if "add_icon" in matcher: - extra_icons += [matcher["add_icon"]] + extra_icon_ids += [matcher["add_icon"]] for key in matcher["tags"].keys(): processed.add(key) if "color" in matcher: @@ -167,26 +170,37 @@ class Scheme: processed.add(key) for tag_key in tags: # type: str - if (tag_key in ["color", "colour"] or tag_key.endswith(":color") or + if (tag_key.endswith(":color") or tag_key.endswith(":colour")): fill = self.get_color(tags[tag_key]) processed.add(tag_key) - if main_icon: - result_set: List[List[str]] = [main_icon] + extra_icons - else: - result_set: List[List[str]] = extra_icons + for tag_key in tags: # type: str + if tag_key in ["color", "colour"]: + fill = self.get_color(tags[tag_key]) + processed.add(tag_key) keys_left = list(filter( lambda x: x not in processed and not self.is_no_drawable(x), tags.keys())) is_default: bool = False - if not result_set and keys_left: - result_set = [[DEFAULT_SHAPE_ID]] + if not main_icon_id and not extra_icon_ids and keys_left: + main_icon_id = [DEFAULT_SHAPE_ID] is_default = True - returned: IconSet = IconSet(result_set, fill, processed, is_default) + main_icon: List[Icon] = [] + if main_icon_id: + main_icon = list(map( + lambda x: icon_extractor.get_path(x)[0], main_icon_id)) + + extra_icons: List[List[Icon]] = [] + for icon_id in extra_icon_ids: + extra_icons.append(list(map( + lambda x: icon_extractor.get_path(x)[0], icon_id))) + + returned: IconSet = IconSet( + main_icon, extra_icons, fill, processed, is_default) self.cache[tags_hash] = returned