diff --git a/map_machine/pictogram/icon.py b/map_machine/pictogram/icon.py index 1e75bc4..d45b129 100644 --- a/map_machine/pictogram/icon.py +++ b/map_machine/pictogram/icon.py @@ -23,7 +23,6 @@ from map_machine.color import is_bright __author__ = "Sergey Vartanov" __email__ = "me@enzet.ru" -DEFAULT_COLOR: Color = Color("#444444") DEFAULT_SHAPE_ID: str = "default" DEFAULT_SMALL_SHAPE_ID: str = "default_small" @@ -359,7 +358,7 @@ class ShapeSpecification: """Specification for shape as a part of an icon.""" shape: Shape - color: Color = DEFAULT_COLOR + color: Color offset: np.ndarray = np.array((0.0, 0.0)) flip_horizontally: bool = False flip_vertically: bool = False diff --git a/map_machine/pictogram/point.py b/map_machine/pictogram/point.py index c70dbcf..a88b8d8 100644 --- a/map_machine/pictogram/point.py +++ b/map_machine/pictogram/point.py @@ -182,7 +182,13 @@ class Point(Tagged): text = text[:26] + ("..." if len(text) > 26 else "") point = self.point + np.array((0.0, self.y + 2.0)) self.draw_text( - svg, text, point, occupied, label.fill, size=label.size + svg, + text, + point, + occupied, + label.fill, + label.size, + label.out_fill, ) def draw_text( @@ -192,8 +198,8 @@ class Point(Tagged): point: np.ndarray, occupied: Optional[Occupied], fill: Color, - size: float = 10.0, - out_fill: Color = Color("white"), + size: float, + out_fill: Color, out_opacity: float = 0.5, out_fill_2: Optional[Color] = None, out_opacity_2: float = 1.0, diff --git a/map_machine/scheme.py b/map_machine/scheme.py index 0138eb8..e430b89 100644 --- a/map_machine/scheme.py +++ b/map_machine/scheme.py @@ -16,7 +16,6 @@ from map_machine.feature.direction import DirectionSet from map_machine.map_configuration import MapConfiguration, LabelMode from map_machine.osm.osm_reader import Tagged, Tags from map_machine.pictogram.icon import ( - DEFAULT_COLOR, DEFAULT_SHAPE_ID, Icon, IconSet, @@ -24,7 +23,7 @@ from map_machine.pictogram.icon import ( ShapeExtractor, ShapeSpecification, ) -from map_machine.text import Label, construct_text +from map_machine.text import Label, TextConstructor __author__ = "Sergey Vartanov" __email__ = "me@enzet.ru" @@ -287,7 +286,7 @@ class RoadMatcher(Matcher): self.border_color: Color = Color( scheme.get_color(structure["border_color"]) ) - self.color: Color = Color("white") + self.color: Color = scheme.get_color("road_color") if "color" in structure: self.color = Color(scheme.get_color(structure["color"])) self.default_width: float = structure["default_width"] @@ -358,6 +357,12 @@ class Scheme: # Storage for created icon sets. self.cache: dict[str, tuple[IconSet, int]] = {} + self.text_constructor: TextConstructor = TextConstructor( + self.get_color("text_color"), + self.get_color("text_main_color"), + self.get_color("text_outline_color"), + ) + @classmethod def from_file(cls, file_name: Path) -> "Scheme": """ @@ -549,7 +554,9 @@ class Scheme: default_shape = extractor.get_shape(DEFAULT_SHAPE_ID) if not main_icon: - main_icon = Icon([ShapeSpecification(default_shape)]) + main_icon = Icon( + [ShapeSpecification(default_shape, self.get_color("default"))] + ) returned: IconSet = IconSet(main_icon, extra_icons, processed) self.cache[tags_hash] = returned, priority @@ -593,11 +600,19 @@ class Scheme: self, tags: Tags, processed: set[str], label_mode: LabelMode ) -> list[Label]: """Construct labels for not processed tags.""" - texts: list[Label] = construct_text(tags, processed, label_mode) + texts: list[Label] = self.text_constructor.construct_text( + tags, processed, label_mode + ) for tag in tags: if self.is_writable(tag, tags[tag]) and tag not in processed: - texts.append(Label(tags[tag])) + texts.append( + Label( + tags[tag], + self.get_color("text_color"), + self.get_color("text_outline_color"), + ) + ) return texts def is_area(self, tags: Tags) -> bool: @@ -624,7 +639,7 @@ class Scheme: structure: Union[str, dict[str, Any]], extractor: ShapeExtractor, groups: dict[str, str] = None, - color: Color = DEFAULT_COLOR, + color: Optional[Color] = None, ) -> ShapeSpecification: """ Parse shape specification from structure, that is just shape string @@ -632,7 +647,9 @@ class Scheme: and offset (optional). """ shape: Shape = extractor.get_shape(DEFAULT_SHAPE_ID) - color: Color = color + color: Color = ( + color if color is not None else Color(self.colors["default"]) + ) offset: np.ndarray = np.array((0.0, 0.0)) flip_horizontally: bool = False flip_vertically: bool = False diff --git a/map_machine/text.py b/map_machine/text.py index c34039b..b3b42ef 100644 --- a/map_machine/text.py +++ b/map_machine/text.py @@ -13,7 +13,6 @@ __author__ = "Sergey Vartanov" __email__ = "me@enzet.ru" DEFAULT_FONT_SIZE: float = 10.0 -DEFAULT_COLOR: Color = Color("#444444") @dataclass @@ -21,7 +20,8 @@ class Label: """Text label.""" text: str - fill: Color = DEFAULT_COLOR + fill: Color + out_fill: Color size: float = DEFAULT_FONT_SIZE @@ -70,111 +70,126 @@ def format_frequency(value: str) -> str: return f"{value} " -def get_text(tags: dict[str, Any], processed: set[str]) -> list[Label]: - """Get text representation of writable tags.""" - texts: list[Label] = [] - values: list[str] = [] +@dataclass +class TextConstructor: - if "voltage:primary" in tags: - values.append(tags["voltage:primary"]) - processed.add("voltage:primary") + default_color: Color + main_color: Color + default_out_color: Color - if "voltage:secondary" in tags: - values.append(tags["voltage:secondary"]) - processed.add("voltage:secondary") - - if "voltage" in tags: - values = tags["voltage"].split(";") - processed.add("voltage") - - if values: - texts.append(Label(", ".join(map(format_voltage, values)))) - - if "frequency" in tags: - text: str = ", ".join( - map(format_frequency, tags["frequency"].split(";")) + def label(self, text: str, size: float = DEFAULT_FONT_SIZE): + return Label( + text, self.default_color, self.default_out_color, size=size ) - texts.append(Label(text)) - processed.add("frequency") - return texts + def get_text( + self, tags: dict[str, Any], processed: set[str] + ) -> list[Label]: + """Get text representation of writable tags.""" + texts: list[Label] = [] + values: list[str] = [] + if "voltage:primary" in tags: + values.append(tags["voltage:primary"]) + processed.add("voltage:primary") -def construct_text( - tags: Tags, processed: set[str], label_mode: LabelMode -) -> list[Label]: - """Construct list of labels from OSM tags.""" + if "voltage:secondary" in tags: + values.append(tags["voltage:secondary"]) + processed.add("voltage:secondary") - texts: list[Label] = [] + if "voltage" in tags: + values = tags["voltage"].split(";") + processed.add("voltage") - name: Optional[str] = None - alternative_name: Optional[str] = None + if values: + texts.append(self.label(", ".join(map(format_voltage, values)))) - if "name" in tags: - name = tags["name"] - processed.add("name") - elif "name:en" in tags: - if not name: - name = tags["name:en"] - processed.add("name:en") - processed.add("name:en") - if "alt_name" in tags: - if alternative_name: - alternative_name += ", " - else: - alternative_name = "" - alternative_name += tags["alt_name"] - processed.add("alt_name") - if "old_name" in tags: - if alternative_name: - alternative_name += ", " - else: - alternative_name = "" - alternative_name += "ex " + tags["old_name"] + if "frequency" in tags: + text: str = ", ".join( + map(format_frequency, tags["frequency"].split(";")) + ) + texts.append(self.label(text)) + processed.add("frequency") - address: list[str] = get_address(tags, processed, label_mode) - - if name: - texts.append(Label(name, Color("black"))) - if alternative_name: - texts.append(Label(f"({alternative_name})")) - if address: - texts.append(Label(", ".join(address))) - - if label_mode == LabelMode.MAIN: return texts - texts += get_text(tags, processed) + def construct_text( + self, tags: Tags, processed: set[str], label_mode: LabelMode + ) -> list[Label]: + """Construct list of labels from OSM tags.""" - if "route_ref" in tags: - texts.append(Label(tags["route_ref"].replace(";", " "))) - processed.add("route_ref") + texts: list[Label] = [] - if "cladr:code" in tags: - texts.append(Label(tags["cladr:code"], size=7.0)) - processed.add("cladr:code") + name: Optional[str] = None + alternative_name: Optional[str] = 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(Label(link, Color("#000088"))) - processed.add("website") + if "name" in tags: + name = tags["name"] + processed.add("name") + elif "name:en" in tags: + if not name: + name = tags["name:en"] + processed.add("name:en") + processed.add("name:en") + if "alt_name" in tags: + if alternative_name: + alternative_name += ", " + else: + alternative_name = "" + alternative_name += tags["alt_name"] + processed.add("alt_name") + if "old_name" in tags: + if alternative_name: + alternative_name += ", " + else: + alternative_name = "" + alternative_name += "ex " + tags["old_name"] - for key in ["phone"]: - if key in tags: - texts.append(Label(tags[key], Color("#444444"))) - processed.add(key) + address: list[str] = get_address(tags, processed, label_mode) - if "height" in tags: - texts.append(Label(f"↕ {tags['height']} m")) - processed.add("height") + if name: + texts.append(Label(name, self.main_color, self.default_out_color)) + if alternative_name: + texts.append(self.label(f"({alternative_name})")) + if address: + texts.append(self.label(", ".join(address))) - return texts + if label_mode == LabelMode.MAIN: + return texts + + texts += self.get_text(tags, processed) + + if "route_ref" in tags: + texts.append(self.label(tags["route_ref"].replace(";", " "))) + processed.add("route_ref") + + if "cladr:code" in tags: + texts.append(self.label(tags["cladr:code"], size=7.0)) + processed.add("cladr:code") + + 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(Label(link, Color("#000088"), self.default_out_color)) + processed.add("website") + + for key in ["phone"]: + if key in tags: + texts.append( + Label(tags[key], Color("#444444"), self.default_out_color) + ) + processed.add(key) + + if "height" in tags: + texts.append(self.label(f"↕ {tags['height']} m")) + processed.add("height") + + return texts