mirror of
https://github.com/enzet/map-machine.git
synced 2025-05-21 21:16:24 +02:00
Refactor text generation.
This commit is contained in:
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 |
BIN
doc/power.png
BIN
doc/power.png
Binary file not shown.
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 115 KiB |
|
@ -19,7 +19,7 @@ To get SVG map, just run
|
|||
|
||||
\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}
|
||||
|
||||
|
@ -94,7 +94,7 @@ Every way and node displayed with the random color picked for each author with \
|
|||
|
||||
\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}).
|
||||
|
||||
|
|
BIN
doc/trees.png
BIN
doc/trees.png
Binary file not shown.
Before Width: | Height: | Size: 166 KiB After Width: | Height: | Size: 167 KiB |
|
@ -21,7 +21,7 @@ To get SVG map, just run
|
|||
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
|
||||
------------
|
||||
|
|
|
@ -128,10 +128,11 @@ def draw_element(target: str, tags_description: str):
|
|||
tags = dict([x.split("=") for x in tags_description.split(",")])
|
||||
scheme = Scheme("data/tags.yml")
|
||||
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"
|
||||
labels = scheme.construct_text(tags, True)
|
||||
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
|
||||
)
|
||||
print(point.is_for_node)
|
||||
|
@ -143,7 +144,7 @@ def draw_element(target: str, tags_description: str):
|
|||
svg.add(path)
|
||||
point.draw_main_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+"))
|
||||
|
||||
|
||||
|
|
|
@ -360,8 +360,10 @@ class Constructor:
|
|||
icon, priority = self.scheme.get_icon(
|
||||
self.icon_extractor, line.tags, for_="line"
|
||||
)
|
||||
labels = self.scheme.construct_text(line.tags, True)
|
||||
|
||||
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
|
||||
))
|
||||
|
||||
|
@ -377,9 +379,10 @@ class Constructor:
|
|||
icon_set: Icon
|
||||
icon_set, priority = self.scheme.get_icon(
|
||||
self.icon_extractor, line.tags)
|
||||
labels = self.scheme.construct_text(line.tags, True)
|
||||
|
||||
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))
|
||||
|
||||
def construct_relations(self) -> None:
|
||||
|
@ -448,14 +451,16 @@ class Constructor:
|
|||
icon_set = Icon([dot], [], color, set(), True)
|
||||
priority = 0
|
||||
draw_outline = False
|
||||
labels = []
|
||||
else:
|
||||
icon_set, priority = self.scheme.get_icon(
|
||||
self.icon_extractor, tags
|
||||
)
|
||||
labels = self.scheme.construct_text(tags, True)
|
||||
|
||||
self.nodes.append(Point(
|
||||
icon_set, tags, flung, node.coordinates, priority=priority,
|
||||
draw_outline=draw_outline
|
||||
icon_set, labels, tags, flung, node.coordinates,
|
||||
priority=priority, draw_outline=draw_outline
|
||||
))
|
||||
|
||||
missing_tags.update(
|
||||
|
|
|
@ -71,7 +71,8 @@ def draw_all_icons(
|
|||
specified_ids |= icons_to_draw
|
||||
print(
|
||||
"Icons with no tag specification: \n " +
|
||||
", ".join(sorted(extractor.icons.keys() - specified_ids)) + ".")
|
||||
", ".join(sorted(extractor.icons.keys() - specified_ids)) + "."
|
||||
)
|
||||
|
||||
draw_grid(
|
||||
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]],
|
||||
extractor: IconExtractor, output_directory: str, columns: int = 16,
|
||||
step: float = 24, color=Color("#444444")
|
||||
) -> List[List[Shape]]:
|
||||
) -> List[List[Shape]]:
|
||||
"""
|
||||
Draw icons in the form of table
|
||||
|
||||
|
|
|
@ -244,20 +244,19 @@ class Painter:
|
|||
)
|
||||
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(
|
||||
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(
|
||||
steps * 2 + index, steps * 3, step=10, text="Drawing texts"
|
||||
)
|
||||
if (self.mode not in [CREATION_TIME_MODE, AUTHOR_MODE] and
|
||||
self.draw_captions != "no"):
|
||||
node.draw_texts(
|
||||
self.svg, self.scheme, occupied, self.draw_captions)
|
||||
point.draw_texts(self.svg, occupied)
|
||||
|
||||
ui.progress_bar(-1, len(nodes), step=10, text="Drawing nodes")
|
||||
|
||||
|
|
|
@ -8,22 +8,10 @@ from colour import Color
|
|||
from roentgen.color import is_bright
|
||||
from roentgen.icon import Shape
|
||||
from roentgen.osm_reader import Tagged
|
||||
from roentgen.scheme import Icon, Scheme
|
||||
from roentgen.text import get_address, get_text
|
||||
from roentgen.scheme import Icon
|
||||
from roentgen.text import Label
|
||||
|
||||
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:
|
||||
|
@ -44,87 +32,6 @@ class Occupied:
|
|||
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:
|
||||
return 0 <= position[0] < len(points) and 0 <= position[1] < len(points[0])
|
||||
|
||||
|
@ -137,8 +44,8 @@ class Point(Tagged):
|
|||
"""
|
||||
|
||||
def __init__(
|
||||
self, icon: Icon, tags: Dict[str, str], point: np.array,
|
||||
coordinates: np.array, priority: float = 0,
|
||||
self, icon: Icon, labels: List[Label], tags: Dict[str, str],
|
||||
point: np.array, coordinates: np.array, priority: float = 0,
|
||||
is_for_node: bool = True, draw_outline: bool = True
|
||||
):
|
||||
super().__init__()
|
||||
|
@ -146,6 +53,7 @@ class Point(Tagged):
|
|||
assert point is not None
|
||||
|
||||
self.icon: Icon = icon
|
||||
self.labels: List[Label] = labels
|
||||
self.tags: Dict[str, str] = tags
|
||||
self.point: np.array = point
|
||||
self.coordinates: np.array = coordinates
|
||||
|
@ -241,16 +149,11 @@ class Point(Tagged):
|
|||
|
||||
return True
|
||||
|
||||
def draw_texts(
|
||||
self, svg: svgwrite.Drawing, scheme: Scheme, occupied: Occupied,
|
||||
draw_captions):
|
||||
def draw_texts(self, svg: svgwrite.Drawing, occupied: Occupied) -> None:
|
||||
"""
|
||||
Draw all labels.
|
||||
"""
|
||||
text_structures: List[TextStruct] = construct_text(
|
||||
self.tags, self.icon.processed, scheme, draw_captions)
|
||||
|
||||
for text_struct in text_structures: # type: TextStruct
|
||||
for text_struct in self.labels: # type: Label
|
||||
text = text_struct.text
|
||||
text = text.replace(""", '"')
|
||||
text = text.replace("&", '&')
|
||||
|
|
|
@ -12,6 +12,7 @@ import yaml
|
|||
from colour import Color
|
||||
|
||||
from roentgen.icon import DEFAULT_SHAPE_ID, Shape, IconExtractor
|
||||
from roentgen.text import Label, get_address, get_text
|
||||
|
||||
DEFAULT_COLOR: Color = Color("#444444")
|
||||
|
||||
|
@ -299,6 +300,85 @@ class Scheme:
|
|||
|
||||
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:
|
||||
for matcher in self.area_tags:
|
||||
if is_matched(matcher, tags):
|
||||
|
|
|
@ -3,8 +3,22 @@ OSM address tag processing.
|
|||
|
||||
Author: Sergey Vartanov (me@enzet.ru).
|
||||
"""
|
||||
from dataclasses import dataclass
|
||||
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]:
|
||||
"""
|
||||
|
|
|
@ -61,7 +61,7 @@ def test_icon_2_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"})
|
||||
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:
|
||||
"""
|
||||
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"})
|
||||
assert icon.main_icon[0].is_default()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue