Add individual icon drawing; refactor.
5
.gitignore
vendored
|
@ -10,9 +10,10 @@
|
||||||
|
|
||||||
# Generated files
|
# Generated files
|
||||||
|
|
||||||
*.png
|
|
||||||
*.pyc
|
*.pyc
|
||||||
*.svg
|
doc/*.html
|
||||||
|
doc/*.svg
|
||||||
|
doc/*.wiki
|
||||||
missed_tags.yml
|
missed_tags.yml
|
||||||
|
|
||||||
# Test scheme files
|
# Test scheme files
|
||||||
|
|
BIN
doc/grid.png
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 66 KiB |
Before Width: | Height: | Size: 149 KiB After Width: | Height: | Size: 144 KiB |
BIN
doc/time.png
Before Width: | Height: | Size: 386 KiB After Width: | Height: | Size: 424 KiB |
BIN
doc/trees.png
Before Width: | Height: | Size: 166 KiB After Width: | Height: | Size: 187 KiB |
BIN
doc/user.png
Before Width: | Height: | Size: 393 KiB After Width: | Height: | Size: 431 KiB |
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 68 KiB |
|
@ -249,8 +249,9 @@ class Constructor:
|
||||||
Röntgen node and way constructor.
|
Röntgen node and way constructor.
|
||||||
"""
|
"""
|
||||||
def __init__(
|
def __init__(
|
||||||
self, check_level, mode: str, seed: str, map_: Map,
|
self, map_: Map, flinger: Flinger, scheme: Scheme,
|
||||||
flinger: Flinger, scheme: Scheme, icon_extractor: IconExtractor):
|
icon_extractor: IconExtractor, check_level=lambda x: True,
|
||||||
|
mode: str = "normal", seed: str = ""):
|
||||||
|
|
||||||
self.check_level = check_level
|
self.check_level = check_level
|
||||||
self.mode: str = mode
|
self.mode: str = mode
|
||||||
|
@ -273,6 +274,14 @@ class Constructor:
|
||||||
self.buildings.append(building)
|
self.buildings.append(building)
|
||||||
self.levels.add(building.get_levels())
|
self.levels.add(building.get_levels())
|
||||||
|
|
||||||
|
def construct(self) -> None:
|
||||||
|
"""
|
||||||
|
Construct nodes, ways, and relations.
|
||||||
|
"""
|
||||||
|
self.construct_ways()
|
||||||
|
self.construct_relations()
|
||||||
|
self.construct_nodes()
|
||||||
|
|
||||||
def construct_ways(self) -> None:
|
def construct_ways(self) -> None:
|
||||||
"""
|
"""
|
||||||
Construct Röntgen ways.
|
Construct Röntgen ways.
|
||||||
|
|
|
@ -7,16 +7,22 @@ import numpy as np
|
||||||
from colour import Color
|
from colour import Color
|
||||||
from svgwrite import Drawing
|
from svgwrite import Drawing
|
||||||
from typing import List, Dict, Any, Set
|
from typing import List, Dict, Any, Set
|
||||||
|
from os.path import join
|
||||||
|
|
||||||
from roentgen.icon import Icon, IconExtractor
|
from roentgen.icon import Icon, IconExtractor
|
||||||
from roentgen.scheme import Scheme
|
from roentgen.scheme import Scheme
|
||||||
|
|
||||||
|
|
||||||
def draw_all_icons(output_file_name: str, columns: int = 16, step: float = 24):
|
def draw_all_icons(
|
||||||
|
output_file_name: str, output_directory: str, columns: int = 16,
|
||||||
|
step: float = 24
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Draw all possible icon combinations in grid.
|
Draw all possible icon combinations in grid.
|
||||||
|
|
||||||
:param output_file_name: output SVG file name for icon grid
|
:param output_file_name: output SVG file name for icon grid
|
||||||
|
:param output_directory: path to the directory to store individual SVG files
|
||||||
|
for icons
|
||||||
:param columns: the number of columns in grid
|
:param columns: the number of columns in grid
|
||||||
:param step: horizontal and vertical distance between icons
|
:param step: horizontal and vertical distance between icons
|
||||||
"""
|
"""
|
||||||
|
@ -66,13 +72,16 @@ def draw_all_icons(output_file_name: str, columns: int = 16, step: float = 24):
|
||||||
"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(output_file_name, to_draw, extractor, columns, step)
|
draw_grid(
|
||||||
|
output_file_name, to_draw, extractor, output_directory, columns, step
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def draw_grid(
|
def draw_grid(
|
||||||
file_name: str, combined_icon_ids: List[Set[str]],
|
file_name: str, combined_icon_ids: List[Set[str]],
|
||||||
extractor: IconExtractor, columns: int = 16, step: float = 24,
|
extractor: IconExtractor, output_directory: str, columns: int = 16,
|
||||||
color=Color("#444444")):
|
step: float = 24, color=Color("#444444")
|
||||||
|
) -> List[List[Icon]]:
|
||||||
"""
|
"""
|
||||||
Draw icons in the form of table
|
Draw icons in the form of table
|
||||||
|
|
||||||
|
@ -80,9 +89,11 @@ def draw_grid(
|
||||||
:param combined_icon_ids: list of set of icon string identifiers
|
:param combined_icon_ids: list of set of icon string identifiers
|
||||||
:param extractor: icon extractor that generates icon SVG path commands using
|
:param extractor: icon extractor that generates icon SVG path commands using
|
||||||
its string identifier
|
its string identifier
|
||||||
|
:param output_directory: path to the directory to store individual SVG files
|
||||||
|
for icons
|
||||||
:param columns: number of columns in grid
|
:param columns: number of columns in grid
|
||||||
:param step: horizontal and vertical distance between icons in grid
|
:param step: horizontal and vertical distance between icons in grid
|
||||||
:return:
|
:param color: icon foreground color
|
||||||
"""
|
"""
|
||||||
point: np.array = np.array((step / 2, step / 2))
|
point: np.array = np.array((step / 2, step / 2))
|
||||||
width: float = step * columns
|
width: float = step * columns
|
||||||
|
@ -92,14 +103,21 @@ def draw_grid(
|
||||||
for icons_to_draw in combined_icon_ids: # type: Set[str]
|
for icons_to_draw in combined_icon_ids: # type: Set[str]
|
||||||
found: bool = False
|
found: bool = False
|
||||||
icon_set: List[Icon] = []
|
icon_set: List[Icon] = []
|
||||||
|
names = []
|
||||||
for icon_id in icons_to_draw: # type: str
|
for icon_id in icons_to_draw: # type: str
|
||||||
icon, extracted = extractor.get_path(icon_id) # type: Icon, bool
|
icon, extracted = extractor.get_path(icon_id) # type: Icon, bool
|
||||||
assert extracted, f"no icon with ID {icon_id}"
|
assert extracted, f"no icon with ID {icon_id}"
|
||||||
icon_set.append(icon)
|
icon_set.append(icon)
|
||||||
found = True
|
found = True
|
||||||
|
if icon.name:
|
||||||
|
names.append(icon.name)
|
||||||
if found:
|
if found:
|
||||||
icons.append(icon_set)
|
icons.append(icon_set)
|
||||||
number += 1
|
number += 1
|
||||||
|
draw_icon(
|
||||||
|
join(output_directory, f"Röntgen {' + '.join(names)}.svg"),
|
||||||
|
icons_to_draw, extractor
|
||||||
|
)
|
||||||
|
|
||||||
height: int = int(int(number / (width / step) + 1) * step)
|
height: int = int(int(number / (width / step) + 1) * step)
|
||||||
svg: Drawing = Drawing(file_name, (width, height))
|
svg: Drawing = Drawing(file_name, (width, height))
|
||||||
|
@ -124,3 +142,26 @@ def draw_grid(
|
||||||
|
|
||||||
with open(file_name, "w") as output_file:
|
with open(file_name, "w") as output_file:
|
||||||
svg.write(output_file)
|
svg.write(output_file)
|
||||||
|
|
||||||
|
return icons
|
||||||
|
|
||||||
|
|
||||||
|
def draw_icon(
|
||||||
|
file_name: str, icon_ids: Set[str], extractor: IconExtractor
|
||||||
|
) -> None:
|
||||||
|
|
||||||
|
icon_set: List[Icon] = []
|
||||||
|
for icon_id in icon_ids: # type: str
|
||||||
|
icon, extracted = extractor.get_path(icon_id) # type: Icon, bool
|
||||||
|
assert extracted, f"no icon with ID {icon_id}"
|
||||||
|
icon_set.append(icon)
|
||||||
|
|
||||||
|
svg: Drawing = Drawing(file_name, (16, 16))
|
||||||
|
|
||||||
|
for icon in icon_set: # type: Icon
|
||||||
|
path = icon.get_path(svg, (8, 8))
|
||||||
|
path.update({"fill": "black"})
|
||||||
|
svg.add(path)
|
||||||
|
|
||||||
|
with open(file_name, "w") as output_file:
|
||||||
|
svg.write(output_file)
|
||||||
|
|
|
@ -4,10 +4,9 @@ Extract icons from SVG file.
|
||||||
Author: Sergey Vartanov (me@enzet.ru).
|
Author: Sergey Vartanov (me@enzet.ru).
|
||||||
"""
|
"""
|
||||||
import re
|
import re
|
||||||
import xml.dom.minidom
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Dict, Any, Set
|
from typing import Dict, Any, Optional
|
||||||
from xml.dom.minidom import Document, Element, Node
|
from xml.dom.minidom import Document, Element, Node, parse
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import svgwrite
|
import svgwrite
|
||||||
|
@ -31,6 +30,7 @@ class Icon:
|
||||||
path: str # SVG icon path
|
path: str # SVG icon path
|
||||||
offset: np.array # vector that should be used to shift the path
|
offset: np.array # vector that should be used to shift the path
|
||||||
id_: str # shape identifier
|
id_: str # shape identifier
|
||||||
|
name: Optional[str] = None # icon description
|
||||||
|
|
||||||
def is_default(self) -> bool:
|
def is_default(self) -> bool:
|
||||||
"""
|
"""
|
||||||
|
@ -96,7 +96,7 @@ class IconExtractor:
|
||||||
self.icons: Dict[str, Icon] = {}
|
self.icons: Dict[str, Icon] = {}
|
||||||
|
|
||||||
with open(svg_file_name) as input_file:
|
with open(svg_file_name) as input_file:
|
||||||
content = xml.dom.minidom.parse(input_file) # type: Document
|
content = parse(input_file) # type: Document
|
||||||
for element in content.childNodes: # type: Element
|
for element in content.childNodes: # type: Element
|
||||||
if element.nodeName != "svg":
|
if element.nodeName != "svg":
|
||||||
continue
|
continue
|
||||||
|
@ -116,14 +116,16 @@ class IconExtractor:
|
||||||
self.parse(sub_node)
|
self.parse(sub_node)
|
||||||
return
|
return
|
||||||
|
|
||||||
if ("id" in node.attributes.keys() and
|
if (node.hasAttribute("id") and node.hasAttribute("d") and
|
||||||
"d" in node.attributes.keys() and
|
node.getAttribute("id")):
|
||||||
node.attributes["id"].value):
|
|
||||||
path: str = node.attributes["d"].value
|
path: str = node.getAttribute("d")
|
||||||
matcher = re.match("[Mm] ([0-9.e-]*)[, ]([0-9.e-]*)", path)
|
matcher = re.match("[Mm] ([0-9.e-]*)[, ]([0-9.e-]*)", path)
|
||||||
if not matcher:
|
if not matcher:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
name: Optional[str] = None
|
||||||
|
|
||||||
def get_offset(value: float):
|
def get_offset(value: float):
|
||||||
""" Get negated icon offset from the origin. """
|
""" Get negated icon offset from the origin. """
|
||||||
return -int(value / GRID_STEP) * GRID_STEP - GRID_STEP / 2
|
return -int(value / GRID_STEP) * GRID_STEP - GRID_STEP / 2
|
||||||
|
@ -132,10 +134,15 @@ class IconExtractor:
|
||||||
get_offset(float(matcher.group(1))),
|
get_offset(float(matcher.group(1))),
|
||||||
get_offset(float(matcher.group(2)))))
|
get_offset(float(matcher.group(2)))))
|
||||||
|
|
||||||
id_: str = node.attributes["id"].value
|
for child_node in node.childNodes:
|
||||||
|
if isinstance(child_node, Element):
|
||||||
|
name = child_node.childNodes[0].nodeValue
|
||||||
|
break
|
||||||
|
|
||||||
|
id_: str = node.getAttribute("id")
|
||||||
matcher = re.match(STANDARD_INKSCAPE_ID, id_)
|
matcher = re.match(STANDARD_INKSCAPE_ID, id_)
|
||||||
if not matcher:
|
if not matcher:
|
||||||
self.icons[id_] = Icon(node.attributes["d"].value, point, id_)
|
self.icons[id_] = Icon(path, point, id_, name)
|
||||||
|
|
||||||
def get_path(self, id_: str) -> (Icon, bool):
|
def get_path(self, id_: str) -> (Icon, bool):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -43,14 +43,13 @@ class Painter:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, show_missing_tags: bool, overlap: int, draw_nodes: bool,
|
self, map_: Map, flinger: Flinger,
|
||||||
mode: str, draw_captions: str, map_: Map, flinger: Flinger,
|
|
||||||
svg: svgwrite.Drawing, icon_extractor: IconExtractor,
|
svg: svgwrite.Drawing, icon_extractor: IconExtractor,
|
||||||
scheme: Scheme):
|
scheme: Scheme, show_missing_tags: bool = False, overlap: int = 12,
|
||||||
|
mode: str = "normal", draw_captions: str = "main"):
|
||||||
|
|
||||||
self.show_missing_tags: bool = show_missing_tags
|
self.show_missing_tags: bool = show_missing_tags
|
||||||
self.overlap: int = overlap
|
self.overlap: int = overlap
|
||||||
self.draw_nodes: bool = draw_nodes
|
|
||||||
self.mode: str = mode
|
self.mode: str = mode
|
||||||
self.draw_captions: str = draw_captions
|
self.draw_captions: str = draw_captions
|
||||||
|
|
||||||
|
@ -60,10 +59,17 @@ class Painter:
|
||||||
self.icon_extractor = icon_extractor
|
self.icon_extractor = icon_extractor
|
||||||
self.scheme: Scheme = scheme
|
self.scheme: Scheme = scheme
|
||||||
|
|
||||||
|
self.background_color: Color = self.scheme.get_color("background_color")
|
||||||
|
if self.mode in [AUTHOR_MODE, CREATION_TIME_MODE]:
|
||||||
|
self.background_color: Color = Color("#111111")
|
||||||
|
|
||||||
def draw(self, constructor: Constructor):
|
def draw(self, constructor: Constructor):
|
||||||
"""
|
"""
|
||||||
Draw map.
|
Draw map.
|
||||||
"""
|
"""
|
||||||
|
self.svg.add(Rect(
|
||||||
|
(0, 0), self.flinger.size, fill=self.background_color))
|
||||||
|
|
||||||
ways = sorted(constructor.figures, key=lambda x: x.line_style.priority)
|
ways = sorted(constructor.figures, key=lambda x: x.line_style.priority)
|
||||||
ways_length: int = len(ways)
|
ways_length: int = len(ways)
|
||||||
for index, way in enumerate(ways): # type: Figure
|
for index, way in enumerate(ways): # type: Figure
|
||||||
|
@ -179,19 +185,16 @@ class Painter:
|
||||||
angle = float(node.get_tag("camera:angle"))
|
angle = float(node.get_tag("camera:angle"))
|
||||||
if "angle" in node.tags:
|
if "angle" in node.tags:
|
||||||
angle = float(node.get_tag("angle"))
|
angle = float(node.get_tag("angle"))
|
||||||
direction_radius: float = (
|
direction_radius: float = (25)
|
||||||
25 * self.flinger.get_scale(node.coordinates))
|
|
||||||
direction_color: Color = (
|
direction_color: Color = (
|
||||||
self.scheme.get_color("direction_camera_color"))
|
self.scheme.get_color("direction_camera_color"))
|
||||||
elif node.get_tag("traffic_sign") == "stop":
|
elif node.get_tag("traffic_sign") == "stop":
|
||||||
direction = node.get_tag("direction")
|
direction = node.get_tag("direction")
|
||||||
direction_radius: float = (
|
direction_radius: float = (25)
|
||||||
25 * self.flinger.get_scale(node.coordinates))
|
|
||||||
direction_color: Color = Color("red")
|
direction_color: Color = Color("red")
|
||||||
else:
|
else:
|
||||||
direction = node.get_tag("direction")
|
direction = node.get_tag("direction")
|
||||||
direction_radius: float = (
|
direction_radius: float = (50)
|
||||||
50 * self.flinger.get_scale(node.coordinates))
|
|
||||||
direction_color: Color = (
|
direction_color: Color = (
|
||||||
self.scheme.get_color("direction_view_color"))
|
self.scheme.get_color("direction_view_color"))
|
||||||
is_revert_gradient = True
|
is_revert_gradient = True
|
||||||
|
@ -238,15 +241,11 @@ class Painter:
|
||||||
continue
|
continue
|
||||||
ui.progress_bar(index, len(nodes), step=10, text="Drawing nodes")
|
ui.progress_bar(index, len(nodes), step=10, text="Drawing nodes")
|
||||||
node.draw_shapes(self.svg, occupied)
|
node.draw_shapes(self.svg, occupied)
|
||||||
ui.progress_bar(-1, len(nodes), step=10, text="Drawing nodes")
|
if (self.mode not in [CREATION_TIME_MODE, AUTHOR_MODE] and
|
||||||
|
self.draw_captions != "no"):
|
||||||
if self.draw_captions == "no":
|
|
||||||
return
|
|
||||||
|
|
||||||
for node in nodes: # type: Point
|
|
||||||
if self.mode not in [CREATION_TIME_MODE, AUTHOR_MODE]:
|
|
||||||
node.draw_texts(
|
node.draw_texts(
|
||||||
self.svg, self.scheme, occupied, self.draw_captions)
|
self.svg, self.scheme, occupied, self.draw_captions)
|
||||||
|
ui.progress_bar(-1, len(nodes), step=10, text="Drawing nodes")
|
||||||
|
|
||||||
|
|
||||||
def check_level_number(tags: Dict[str, Any], level: float):
|
def check_level_number(tags: Dict[str, Any], level: float):
|
||||||
|
@ -295,7 +294,8 @@ def main(argv) -> None:
|
||||||
"""
|
"""
|
||||||
if len(argv) == 2:
|
if len(argv) == 2:
|
||||||
if argv[1] == "grid":
|
if argv[1] == "grid":
|
||||||
draw_all_icons("icon_grid.svg")
|
os.makedirs("icon_set", exist_ok=True)
|
||||||
|
draw_all_icons("icon_grid.svg", "icon_set")
|
||||||
return
|
return
|
||||||
|
|
||||||
options: argparse.Namespace = ui.parse_options(argv)
|
options: argparse.Namespace = ui.parse_options(argv)
|
||||||
|
@ -303,10 +303,6 @@ def main(argv) -> None:
|
||||||
if not options:
|
if not options:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
background_color: Color = Color("#EEEEEE")
|
|
||||||
if options.mode in [AUTHOR_MODE, CREATION_TIME_MODE]:
|
|
||||||
background_color: Color = Color("#111111")
|
|
||||||
|
|
||||||
if options.input_file_name:
|
if options.input_file_name:
|
||||||
input_file_name = options.input_file_name
|
input_file_name = options.input_file_name
|
||||||
else:
|
else:
|
||||||
|
@ -325,8 +321,7 @@ def main(argv) -> None:
|
||||||
max1 = np.array((map_.boundary_box[0].max_, map_.boundary_box[1].max_))
|
max1 = np.array((map_.boundary_box[0].max_, map_.boundary_box[1].max_))
|
||||||
else:
|
else:
|
||||||
|
|
||||||
boundary_box = list(map(
|
boundary_box = list(map(float, options.boundary_box.split(',')))
|
||||||
lambda x: float(x.replace('m', '-')), options.boundary_box.split(',')))
|
|
||||||
|
|
||||||
full = False # Full keys getting
|
full = False # Full keys getting
|
||||||
|
|
||||||
|
@ -340,9 +335,7 @@ def main(argv) -> None:
|
||||||
print("Fatal: no such file: " + file_name + ".")
|
print("Fatal: no such file: " + file_name + ".")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
osm_reader.parse_osm_file(
|
osm_reader.parse_osm_file(file_name, full=full)
|
||||||
file_name, parse_ways=options.draw_ways,
|
|
||||||
parse_relations=options.draw_ways, full=full)
|
|
||||||
|
|
||||||
map_: Map = osm_reader.map_
|
map_: Map = osm_reader.map_
|
||||||
|
|
||||||
|
@ -354,7 +347,6 @@ def main(argv) -> None:
|
||||||
|
|
||||||
svg: svgwrite.Drawing = (
|
svg: svgwrite.Drawing = (
|
||||||
svgwrite.Drawing(options.output_file_name, size=size))
|
svgwrite.Drawing(options.output_file_name, size=size))
|
||||||
svg.add(Rect((0, 0), size, fill=background_color))
|
|
||||||
|
|
||||||
icon_extractor: IconExtractor = IconExtractor(ICONS_FILE_NAME)
|
icon_extractor: IconExtractor = IconExtractor(ICONS_FILE_NAME)
|
||||||
|
|
||||||
|
@ -375,18 +367,13 @@ def main(argv) -> None:
|
||||||
return not check_level_number(x, float(options.level))
|
return not check_level_number(x, float(options.level))
|
||||||
|
|
||||||
constructor: Constructor = Constructor(
|
constructor: Constructor = Constructor(
|
||||||
check_level, options.mode, options.seed, map_, flinger, scheme,
|
map_, flinger, scheme, icon_extractor, check_level, options.mode,
|
||||||
icon_extractor)
|
options.seed)
|
||||||
if options.draw_ways:
|
constructor.construct()
|
||||||
constructor.construct_ways()
|
|
||||||
constructor.construct_relations()
|
|
||||||
if options.mode not in [AUTHOR_MODE, CREATION_TIME_MODE]:
|
|
||||||
constructor.construct_nodes()
|
|
||||||
|
|
||||||
painter: Painter = Painter(
|
painter: Painter = Painter(
|
||||||
show_missing_tags=options.show_missing_tags, overlap=options.overlap,
|
show_missing_tags=options.show_missing_tags, overlap=options.overlap,
|
||||||
draw_nodes=options.draw_nodes, mode=options.mode,
|
mode=options.mode, draw_captions=options.draw_captions,
|
||||||
draw_captions=options.draw_captions,
|
|
||||||
map_=map_, flinger=flinger, svg=svg, icon_extractor=icon_extractor,
|
map_=map_, flinger=flinger, svg=svg, icon_extractor=icon_extractor,
|
||||||
scheme=scheme)
|
scheme=scheme)
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,8 @@ class OSMNode(Tagged):
|
||||||
|
|
||||||
self.id_ = structure["id"]
|
self.id_ = structure["id"]
|
||||||
self.coordinates = np.array((structure["lat"], structure["lon"]))
|
self.coordinates = np.array((structure["lat"], structure["lon"]))
|
||||||
self.tags = structure["tags"]
|
if "tags" in structure:
|
||||||
|
self.tags = structure["tags"]
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
@ -119,6 +120,16 @@ class OSMWay(Tagged):
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def parse_from_structure(self, structure: Dict[str, Any], nodes) -> "OSMWay":
|
||||||
|
|
||||||
|
self.id_ = structure["id"]
|
||||||
|
for node_id in structure["nodes"]:
|
||||||
|
self.nodes.append(nodes[node_id])
|
||||||
|
if "tags" in structure:
|
||||||
|
self.tags = structure["tags"]
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
def is_cycle(self) -> bool:
|
def is_cycle(self) -> bool:
|
||||||
"""
|
"""
|
||||||
Is way a cycle way or an area boundary.
|
Is way a cycle way or an area boundary.
|
||||||
|
@ -171,16 +182,37 @@ class OSMRelation(Tagged):
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def parse_from_structure(self, structure: Dict[str, Any]) -> "OSMRelation":
|
||||||
|
|
||||||
|
self.id_ = structure["id"]
|
||||||
|
for member in structure["members"]:
|
||||||
|
mem = OSMMember()
|
||||||
|
mem.type_ = member["type"]
|
||||||
|
mem.role = member["role"]
|
||||||
|
mem.ref = member["ref"]
|
||||||
|
self.members.append(mem)
|
||||||
|
if "tags" in structure:
|
||||||
|
self.tags = structure["tags"]
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
class OSMMember:
|
class OSMMember:
|
||||||
"""
|
"""
|
||||||
Member of OpenStreetMap relation.
|
Member of OpenStreetMap relation.
|
||||||
"""
|
"""
|
||||||
def __init__(self, text: str):
|
def __init__(self):
|
||||||
|
self.type_ = ""
|
||||||
|
self.ref = 0
|
||||||
|
self.role = ""
|
||||||
|
|
||||||
|
def parse_from_xml(self, text: str) -> "OSMMember":
|
||||||
self.type_: str = get_value("type", text)
|
self.type_: str = get_value("type", text)
|
||||||
self.ref: int = int(get_value("ref", text))
|
self.ref: int = int(get_value("ref", text))
|
||||||
self.role: str = get_value("role", text)
|
self.role: str = get_value("role", text)
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
def get_value(key: str, text: str):
|
def get_value(key: str, text: str):
|
||||||
"""
|
"""
|
||||||
|
@ -239,14 +271,29 @@ class OverpassReader:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.map_ = Map()
|
self.map_ = Map()
|
||||||
|
|
||||||
def parse_json_file(self, file_name: str):
|
def parse_json_file(self, file_name: str) -> Map:
|
||||||
with open(file_name) as input_file:
|
with open(file_name) as input_file:
|
||||||
structure = json.load(input_file)
|
structure = json.load(input_file)
|
||||||
|
|
||||||
|
node_map = {}
|
||||||
|
way_map = {}
|
||||||
|
|
||||||
for element in structure["elements"]:
|
for element in structure["elements"]:
|
||||||
if element["type"] == "node":
|
if element["type"] == "node":
|
||||||
node = OSMNode().parse_from_structure(element)
|
node = OSMNode().parse_from_structure(element)
|
||||||
|
node_map[node.id_] = node
|
||||||
self.map_.add_node(node)
|
self.map_.add_node(node)
|
||||||
|
for element in structure["elements"]:
|
||||||
|
if element["type"] == "way":
|
||||||
|
way = OSMWay().parse_from_structure(element, node_map)
|
||||||
|
way_map[way.id_] = way
|
||||||
|
self.map_.add_way(way)
|
||||||
|
for element in structure["elements"]:
|
||||||
|
if element["type"] == "relation":
|
||||||
|
relation = OSMRelation().parse_from_structure(element)
|
||||||
|
self.map_.add_relation(relation)
|
||||||
|
|
||||||
|
return self.map_
|
||||||
|
|
||||||
|
|
||||||
class OSMReader:
|
class OSMReader:
|
||||||
|
@ -334,7 +381,7 @@ class OSMReader:
|
||||||
element.nodes.append(
|
element.nodes.append(
|
||||||
self.map_.node_map[int(get_value("ref", line))])
|
self.map_.node_map[int(get_value("ref", line))])
|
||||||
elif line.startswith("<member"):
|
elif line.startswith("<member"):
|
||||||
element.members.append(OSMMember(line))
|
element.members.append(OSMMember().parse_from_xml(line))
|
||||||
|
|
||||||
progress_bar(-1, lines_number, text="Parsing")
|
progress_bar(-1, lines_number, text="Parsing")
|
||||||
|
|
||||||
|
|
|
@ -254,13 +254,15 @@ class Scheme:
|
||||||
for key in element: # type: str
|
for key in element: # type: str
|
||||||
if key not in [
|
if key not in [
|
||||||
"tags", "no_tags", "priority", "level", "icon",
|
"tags", "no_tags", "priority", "level", "icon",
|
||||||
"r", "r2"]:
|
"r", "r1", "r2"]:
|
||||||
value = element[key]
|
value = element[key]
|
||||||
if isinstance(value, str) and value.endswith("_color"):
|
if isinstance(value, str) and value.endswith("_color"):
|
||||||
value = self.get_color(value)
|
value = self.get_color(value)
|
||||||
style[key] = value
|
style[key] = value
|
||||||
if "r" in element:
|
if "r" in element:
|
||||||
style["stroke-width"] = (element["r"] * scale)
|
style["stroke-width"] = (element["r"] * scale)
|
||||||
|
if "r1" in element:
|
||||||
|
style["stroke-width"] = (element["r1"] * scale + 1)
|
||||||
if "r2" in element:
|
if "r2" in element:
|
||||||
style["stroke-width"] = (element["r2"] * scale + 2)
|
style["stroke-width"] = (element["r2"] * scale + 2)
|
||||||
|
|
||||||
|
|
|
@ -41,16 +41,6 @@ def parse_options(args) -> argparse.Namespace:
|
||||||
default=18,
|
default=18,
|
||||||
dest="scale",
|
dest="scale",
|
||||||
type=float)
|
type=float)
|
||||||
parser.add_argument(
|
|
||||||
"-nn", "--no-draw-nodes",
|
|
||||||
dest="draw_nodes",
|
|
||||||
action="store_false",
|
|
||||||
default=True)
|
|
||||||
parser.add_argument(
|
|
||||||
"-nw", "--no-draw-ways",
|
|
||||||
dest="draw_ways",
|
|
||||||
action="store_false",
|
|
||||||
default=True)
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--captions", "--no-draw-captions",
|
"--captions", "--no-draw-captions",
|
||||||
dest="draw_captions",
|
dest="draw_captions",
|
||||||
|
@ -86,6 +76,26 @@ def parse_options(args) -> argparse.Namespace:
|
||||||
return arguments
|
return arguments
|
||||||
|
|
||||||
|
|
||||||
|
def progress_bar1(
|
||||||
|
number: int, total: int, length: int = 20, step: int = 1000) -> None:
|
||||||
|
"""
|
||||||
|
Draw progress bar using Unicode symbols.
|
||||||
|
|
||||||
|
:param number: current value
|
||||||
|
:param total: maximum value
|
||||||
|
:param length: progress bar length.
|
||||||
|
:param step: frequency of progress bar updating (assuming that numbers go
|
||||||
|
subsequently)
|
||||||
|
:param text: short description
|
||||||
|
"""
|
||||||
|
ratio: float = number / total
|
||||||
|
parts: int = int(ratio * length * BOXES_LENGTH)
|
||||||
|
fill_length: int = int(parts / BOXES_LENGTH)
|
||||||
|
box: str = BOXES[int(parts - fill_length * BOXES_LENGTH)]
|
||||||
|
return (
|
||||||
|
f"{fill_length * '█'}{box}{int(length - fill_length - 1) * ' '}")
|
||||||
|
|
||||||
|
|
||||||
def progress_bar(
|
def progress_bar(
|
||||||
number: int, total: int, length: int = 20, step: int = 1000,
|
number: int, total: int, length: int = 20, step: int = 1000,
|
||||||
text: str = "") -> None:
|
text: str = "") -> None:
|
||||||
|
|
|
@ -33,3 +33,6 @@ class MinMax:
|
||||||
Get middle point between minimum and maximum.
|
Get middle point between minimum and maximum.
|
||||||
"""
|
"""
|
||||||
return (self.min_ + self.max_) / 2
|
return (self.min_ + self.max_) / 2
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"{self.min_}:{self.max_}"
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
"""
|
"""
|
||||||
Author: Sergey Vartanov (me@enzet.ru).
|
Author: Sergey Vartanov (me@enzet.ru).
|
||||||
"""
|
"""
|
||||||
|
from os import makedirs
|
||||||
|
|
||||||
from roentgen.grid import draw_all_icons
|
from roentgen.grid import draw_all_icons
|
||||||
|
|
||||||
|
|
||||||
def test_icons() -> None:
|
def test_icons() -> None:
|
||||||
""" Test grid drawing. """
|
""" Test grid drawing. """
|
||||||
draw_all_icons("temp.svg")
|
makedirs("icon_set", exist_ok=True)
|
||||||
|
draw_all_icons("temp.svg", "icon_set")
|
||||||
|
|