Refactor text generation.

This commit is contained in:
Sergey Vartanov 2021-05-06 03:20:38 +03:00
parent f317eba64b
commit 958a85fe0a
13 changed files with 129 additions and 126 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 86 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 115 KiB

Before After
Before After

View file

@ -19,7 +19,7 @@ To get SVG map, just run
\code {python roentgen.py -b <lon1>,<lat1>,<lon2>,<lat2>} {bash} \code {python roentgen.py -b <lon1>,<lat1>,<lon2>,<lat2>} {bash}
(e.g. \tt {python roentgen.py -b 2.284,48.86,2.29,48.865}). It will automatically download OSM data and write output map to \tt {map.svg}. For more options see \href {#map-generation} {Map Generation section}. (e.g. \tt {python roentgen.py -b 2.284,48.86,2.29,48.865}). It will automatically download OSM data and write output map to \tt {map.svg}. For more options see \href {#map-generation} {Map Generation}.
\2 {Map features} {features} \2 {Map features} {features}
@ -94,7 +94,7 @@ Every way and node displayed with the random color picked for each author with \
\image {doc/user.png} {Author mode} \image {doc/user.png} {Author mode}
\2 {Map generation} {map_generation} \2 {Map generation} {map-generation}
\b {Requirements}\: Python (at least 3.8) and Python libraries (install everything using \tt {pip install -r requirements.txt}). \b {Requirements}\: Python (at least 3.8) and Python libraries (install everything using \tt {pip install -r requirements.txt}).

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

After

Width:  |  Height:  |  Size: 167 KiB

Before After
Before After

View file

@ -21,7 +21,7 @@ To get SVG map, just run
python roentgen.py -b <lon1>,<lat1>,<lon2>,<lat2> python roentgen.py -b <lon1>,<lat1>,<lon2>,<lat2>
``` ```
(e.g. `python roentgen.py -b 2.284,48.86,2.29,48.865`). It will automatically download OSM data and write output map to `map.svg`. For more options see [Map Generation section](#map-generation). (e.g. `python roentgen.py -b 2.284,48.86,2.29,48.865`). It will automatically download OSM data and write output map to `map.svg`. For more options see [Map Generation](#map-generation).
Map features Map features
------------ ------------

View file

@ -128,10 +128,11 @@ def draw_element(target: str, tags_description: str):
tags = dict([x.split("=") for x in tags_description.split(",")]) tags = dict([x.split("=") for x in tags_description.split(",")])
scheme = Scheme("data/tags.yml") scheme = Scheme("data/tags.yml")
icon_extractor = IconExtractor("icons/icons.svg") icon_extractor = IconExtractor("icons/icons.svg")
icon_set, priority = scheme.get_icon(icon_extractor, tags) icon, priority = scheme.get_icon(icon_extractor, tags)
is_for_node: bool = target == "node" is_for_node: bool = target == "node"
labels = scheme.construct_text(tags, True)
point = Point( point = Point(
icon_set, tags, np.array((32, 32)), None, is_for_node=is_for_node, icon, labels, tags, np.array((32, 32)), None, is_for_node=is_for_node,
draw_outline=is_for_node draw_outline=is_for_node
) )
print(point.is_for_node) print(point.is_for_node)
@ -143,7 +144,7 @@ def draw_element(target: str, tags_description: str):
svg.add(path) svg.add(path)
point.draw_main_shapes(svg) point.draw_main_shapes(svg)
point.draw_extra_shapes(svg) point.draw_extra_shapes(svg)
point.draw_texts(svg, scheme, None, True) point.draw_texts(svg, None)
svg.write(open("test_icon.svg", "w+")) svg.write(open("test_icon.svg", "w+"))

View file

@ -360,8 +360,10 @@ class Constructor:
icon, priority = self.scheme.get_icon( icon, priority = self.scheme.get_icon(
self.icon_extractor, line.tags, for_="line" self.icon_extractor, line.tags, for_="line"
) )
labels = self.scheme.construct_text(line.tags, True)
self.nodes.append(Point( self.nodes.append(Point(
icon, line.tags, center_point, center_coordinates, icon, labels, line.tags, center_point, center_coordinates,
is_for_node=False, priority=priority is_for_node=False, priority=priority
)) ))
@ -377,9 +379,10 @@ class Constructor:
icon_set: Icon icon_set: Icon
icon_set, priority = self.scheme.get_icon( icon_set, priority = self.scheme.get_icon(
self.icon_extractor, line.tags) self.icon_extractor, line.tags)
labels = self.scheme.construct_text(line.tags, True)
self.nodes.append(Point( self.nodes.append(Point(
icon_set, line.tags, center_point, center_coordinates, icon_set, labels, line.tags, center_point, center_coordinates,
is_for_node=False, priority=priority)) is_for_node=False, priority=priority))
def construct_relations(self) -> None: def construct_relations(self) -> None:
@ -448,14 +451,16 @@ class Constructor:
icon_set = Icon([dot], [], color, set(), True) icon_set = Icon([dot], [], color, set(), True)
priority = 0 priority = 0
draw_outline = False draw_outline = False
labels = []
else: else:
icon_set, priority = self.scheme.get_icon( icon_set, priority = self.scheme.get_icon(
self.icon_extractor, tags self.icon_extractor, tags
) )
labels = self.scheme.construct_text(tags, True)
self.nodes.append(Point( self.nodes.append(Point(
icon_set, tags, flung, node.coordinates, priority=priority, icon_set, labels, tags, flung, node.coordinates,
draw_outline=draw_outline priority=priority, draw_outline=draw_outline
)) ))
missing_tags.update( missing_tags.update(

View file

@ -71,7 +71,8 @@ def draw_all_icons(
specified_ids |= icons_to_draw specified_ids |= icons_to_draw
print( print(
"Icons with no tag specification: \n " + "Icons with no tag specification: \n " +
", ".join(sorted(extractor.icons.keys() - specified_ids)) + ".") ", ".join(sorted(extractor.icons.keys() - specified_ids)) + "."
)
draw_grid( draw_grid(
output_file_name, to_draw, extractor, output_directory, columns, step output_file_name, to_draw, extractor, output_directory, columns, step
@ -82,7 +83,7 @@ def draw_grid(
file_name: str, combined_icon_ids: List[Set[str]], file_name: str, combined_icon_ids: List[Set[str]],
extractor: IconExtractor, output_directory: str, columns: int = 16, extractor: IconExtractor, output_directory: str, columns: int = 16,
step: float = 24, color=Color("#444444") step: float = 24, color=Color("#444444")
) -> List[List[Shape]]: ) -> List[List[Shape]]:
""" """
Draw icons in the form of table Draw icons in the form of table

View file

@ -244,20 +244,19 @@ class Painter:
) )
node.draw_main_shapes(self.svg, occupied) node.draw_main_shapes(self.svg, occupied)
for index, node in enumerate(nodes): # type: int, Point for index, point in enumerate(nodes): # type: int, Point
ui.progress_bar( ui.progress_bar(
steps + index, steps * 3, step=10, text="Drawing extra icons" steps + index, steps * 3, step=10, text="Drawing extra icons"
) )
node.draw_extra_shapes(self.svg, occupied) point.draw_extra_shapes(self.svg, occupied)
for index, node in enumerate(nodes): # type: int, Point for index, point in enumerate(nodes): # type: int, Point
ui.progress_bar( ui.progress_bar(
steps * 2 + index, steps * 3, step=10, text="Drawing texts" steps * 2 + index, steps * 3, step=10, text="Drawing texts"
) )
if (self.mode not in [CREATION_TIME_MODE, AUTHOR_MODE] and if (self.mode not in [CREATION_TIME_MODE, AUTHOR_MODE] and
self.draw_captions != "no"): self.draw_captions != "no"):
node.draw_texts( point.draw_texts(self.svg, occupied)
self.svg, self.scheme, occupied, self.draw_captions)
ui.progress_bar(-1, len(nodes), step=10, text="Drawing nodes") ui.progress_bar(-1, len(nodes), step=10, text="Drawing nodes")

View file

@ -8,22 +8,10 @@ from colour import Color
from roentgen.color import is_bright from roentgen.color import is_bright
from roentgen.icon import Shape from roentgen.icon import Shape
from roentgen.osm_reader import Tagged from roentgen.osm_reader import Tagged
from roentgen.scheme import Icon, Scheme from roentgen.scheme import Icon
from roentgen.text import get_address, get_text from roentgen.text import Label
DEFAULT_FONT: str = "Roboto" 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
class Occupied: class Occupied:
@ -44,87 +32,6 @@ class Occupied:
assert self.matrix[point[0], point[1]] == True assert self.matrix[point[0], point[1]] == True
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 += "ex " + 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
for text in get_text(tags): # type: str
if text:
texts.append(TextStruct(text))
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
def in_range(position, points) -> bool: def in_range(position, points) -> bool:
return 0 <= position[0] < len(points) and 0 <= position[1] < len(points[0]) return 0 <= position[0] < len(points) and 0 <= position[1] < len(points[0])
@ -137,8 +44,8 @@ class Point(Tagged):
""" """
def __init__( def __init__(
self, icon: Icon, tags: Dict[str, str], point: np.array, self, icon: Icon, labels: List[Label], tags: Dict[str, str],
coordinates: np.array, priority: float = 0, point: np.array, coordinates: np.array, priority: float = 0,
is_for_node: bool = True, draw_outline: bool = True is_for_node: bool = True, draw_outline: bool = True
): ):
super().__init__() super().__init__()
@ -146,6 +53,7 @@ class Point(Tagged):
assert point is not None assert point is not None
self.icon: Icon = icon self.icon: Icon = icon
self.labels: List[Label] = labels
self.tags: Dict[str, str] = tags self.tags: Dict[str, str] = tags
self.point: np.array = point self.point: np.array = point
self.coordinates: np.array = coordinates self.coordinates: np.array = coordinates
@ -241,16 +149,11 @@ class Point(Tagged):
return True return True
def draw_texts( def draw_texts(self, svg: svgwrite.Drawing, occupied: Occupied) -> None:
self, svg: svgwrite.Drawing, scheme: Scheme, occupied: Occupied,
draw_captions):
""" """
Draw all labels. Draw all labels.
""" """
text_structures: List[TextStruct] = construct_text( for text_struct in self.labels: # type: Label
self.tags, self.icon.processed, scheme, draw_captions)
for text_struct in text_structures: # type: TextStruct
text = text_struct.text text = text_struct.text
text = text.replace("&quot;", '"') text = text.replace("&quot;", '"')
text = text.replace("&amp;", '&') text = text.replace("&amp;", '&')

View file

@ -12,6 +12,7 @@ import yaml
from colour import Color from colour import Color
from roentgen.icon import DEFAULT_SHAPE_ID, Shape, IconExtractor from roentgen.icon import DEFAULT_SHAPE_ID, Shape, IconExtractor
from roentgen.text import Label, get_address, get_text
DEFAULT_COLOR: Color = Color("#444444") DEFAULT_COLOR: Color = Color("#444444")
@ -299,6 +300,85 @@ class Scheme:
return line_styles return line_styles
def construct_text(self, tags, draw_captions) -> List[Label]:
"""
Construct labels for not processed tags.
"""
texts: List[Label] = []
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 += "ex " + tags["old_name"]
address: List[str] = get_address(tags, draw_captions)
if name:
texts.append(Label(name, Color("black")))
if alt_name:
texts.append(Label(f"({alt_name})"))
if address:
texts.append(Label(", ".join(address)))
if draw_captions == "main":
return texts
for text in get_text(tags): # type: str
if text:
texts.append(Label(text))
if "route_ref" in tags:
texts.append(Label(tags["route_ref"].replace(";", " ")))
tags.pop("route_ref", None)
if "cladr:code" in tags:
texts.append(Label(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(Label(link, Color("#000088")))
tags.pop("website", None)
for k in ["phone"]:
if k in tags:
texts.append(Label(tags[k], Color("#444444")))
tags.pop(k)
for tag in tags:
if self.is_writable(tag):
texts.append(Label(tags[tag]))
return texts
def is_area(self, tags: Dict[str, str]) -> bool: def is_area(self, tags: Dict[str, str]) -> bool:
for matcher in self.area_tags: for matcher in self.area_tags:
if is_matched(matcher, tags): if is_matched(matcher, tags):

View file

@ -3,8 +3,22 @@ OSM address tag processing.
Author: Sergey Vartanov (me@enzet.ru). Author: Sergey Vartanov (me@enzet.ru).
""" """
from dataclasses import dataclass
from typing import Any, Dict, List from typing import Any, Dict, List
from colour import Color
DEFAULT_COLOR: Color = Color("#444444")
@dataclass
class Label:
"""
Text label.
"""
text: str
fill: Color = DEFAULT_COLOR
size: float = 10.0
def get_address(tags: Dict[str, Any], draw_captions_mode: str) -> List[str]: def get_address(tags: Dict[str, Any], draw_captions_mode: str) -> List[str]:
""" """

View file

@ -61,7 +61,7 @@ def test_icon_2_extra() -> None:
def __test_no_icon_1_extra() -> None: def __test_no_icon_1_extra() -> None:
""" """
Tags that should be visualized with defult main icon and single extra icon. Tags that should be visualized with default main icon and single extra icon.
""" """
icon = get_icon({"access": "private"}) icon = get_icon({"access": "private"})
assert icon.main_icon[0].is_default() assert icon.main_icon[0].is_default()
@ -70,7 +70,7 @@ def __test_no_icon_1_extra() -> None:
def __test_no_icon_2_extra() -> None: def __test_no_icon_2_extra() -> None:
""" """
Tags that should be visualized with defult main icon and two extra icons. Tags that should be visualized with default main icon and two extra icons.
""" """
icon = get_icon({"access": "private", "bicycle": "yes"}) icon = get_icon({"access": "private", "bicycle": "yes"})
assert icon.main_icon[0].is_default() assert icon.main_icon[0].is_default()