Support color for icon shape.
* Create icon shape specification with color. * Change icon generation rules. * Add height label processing.
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 93 KiB |
BIN
doc/grid.png
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
BIN
doc/power.png
Before Width: | Height: | Size: 115 KiB After Width: | Height: | Size: 116 KiB |
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 145 KiB |
|
@ -19,7 +19,8 @@ from roentgen.osm_reader import (
|
||||||
Map, OSMMember, OSMNode, OSMRelation, OSMWay, Tagged
|
Map, OSMMember, OSMNode, OSMRelation, OSMWay, Tagged
|
||||||
)
|
)
|
||||||
from roentgen.point import Point
|
from roentgen.point import Point
|
||||||
from roentgen.scheme import Icon, LineStyle, Scheme
|
from roentgen.scheme import Icon, LineStyle, Scheme, ShapeSpecification, \
|
||||||
|
DEFAULT_COLOR
|
||||||
from roentgen.util import MinMax
|
from roentgen.util import MinMax
|
||||||
|
|
||||||
DEBUG: bool = False
|
DEBUG: bool = False
|
||||||
|
@ -445,34 +446,35 @@ class Constructor:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
priority: int
|
priority: int
|
||||||
icon_set: Icon
|
icon: Icon
|
||||||
draw_outline: bool = True
|
draw_outline: bool = True
|
||||||
|
|
||||||
if self.mode in ["time", "user-coloring"]:
|
if self.mode in ["time", "user-coloring"]:
|
||||||
if not tags:
|
if not tags:
|
||||||
continue
|
continue
|
||||||
|
color = DEFAULT_COLOR
|
||||||
if self.mode == "user-coloring":
|
if self.mode == "user-coloring":
|
||||||
color = get_user_color(node.user, self.seed)
|
color = get_user_color(node.user, self.seed)
|
||||||
if self.mode == "time":
|
if self.mode == "time":
|
||||||
color = get_time_color(node.timestamp, self.map_.time)
|
color = get_time_color(node.timestamp, self.map_.time)
|
||||||
dot, _ = self.icon_extractor.get_path(DEFAULT_SMALL_SHAPE_ID)
|
dot, _ = self.icon_extractor.get_path(DEFAULT_SMALL_SHAPE_ID)
|
||||||
icon_set = Icon([dot], [], color, set(), True)
|
icon = Icon([ShapeSpecification(dot, color)], [], set())
|
||||||
priority = 0
|
priority = 0
|
||||||
draw_outline = False
|
draw_outline = False
|
||||||
labels = []
|
labels = []
|
||||||
else:
|
else:
|
||||||
icon_set, priority = self.scheme.get_icon(
|
icon, priority = self.scheme.get_icon(
|
||||||
self.icon_extractor, tags
|
self.icon_extractor, tags
|
||||||
)
|
)
|
||||||
labels = self.scheme.construct_text(tags, True)
|
labels = self.scheme.construct_text(tags, True)
|
||||||
|
|
||||||
self.nodes.append(Point(
|
self.nodes.append(Point(
|
||||||
icon_set, labels, tags, flung, node.coordinates,
|
icon, labels, tags, flung, node.coordinates,
|
||||||
priority=priority, draw_outline=draw_outline
|
priority=priority, draw_outline=draw_outline
|
||||||
))
|
))
|
||||||
|
|
||||||
missing_tags.update(
|
missing_tags.update(
|
||||||
f"{key}: {tags[key]}" for key in tags
|
f"{key}: {tags[key]}" for key in tags
|
||||||
if key not in icon_set.processed)
|
if key not in icon.processed)
|
||||||
|
|
||||||
ui.progress_bar(-1, len(self.map_.node_map), text="Constructing nodes")
|
ui.progress_bar(-1, len(self.map_.node_map), text="Constructing nodes")
|
||||||
|
|
|
@ -11,7 +11,7 @@ from colour import Color
|
||||||
from svgwrite import Drawing
|
from svgwrite import Drawing
|
||||||
|
|
||||||
from roentgen.icon import IconExtractor, Shape
|
from roentgen.icon import IconExtractor, Shape
|
||||||
from roentgen.scheme import Scheme
|
from roentgen.scheme import Scheme, ShapeSpecification
|
||||||
|
|
||||||
|
|
||||||
def draw_all_icons(
|
def draw_all_icons(
|
||||||
|
@ -35,9 +35,18 @@ def draw_all_icons(
|
||||||
|
|
||||||
to_draw: List[Set[str]] = []
|
to_draw: List[Set[str]] = []
|
||||||
|
|
||||||
|
icons_file_name: str = "icons/icons.svg"
|
||||||
|
extractor: IconExtractor = IconExtractor(icons_file_name)
|
||||||
|
|
||||||
for element in scheme.icons: # type: Dict[str, Any]
|
for element in scheme.icons: # type: Dict[str, Any]
|
||||||
if "icon" in element and set(element["icon"]) not in to_draw:
|
if "icon" in element:
|
||||||
to_draw.append(set(element["icon"]))
|
specifications = [
|
||||||
|
ShapeSpecification.from_structure(x, extractor, scheme)
|
||||||
|
for x in element["icon"]
|
||||||
|
]
|
||||||
|
ids = set(x.shape.id_ for x in specifications)
|
||||||
|
if ids not in to_draw:
|
||||||
|
to_draw.append(ids)
|
||||||
if "add_icon" in element and set(element["add_icon"]) not in to_draw:
|
if "add_icon" in element and set(element["add_icon"]) not in to_draw:
|
||||||
to_draw.append(set(element["add_icon"]))
|
to_draw.append(set(element["add_icon"]))
|
||||||
if "over_icon" not in element:
|
if "over_icon" not in element:
|
||||||
|
@ -65,16 +74,13 @@ def draw_all_icons(
|
||||||
current_set not in to_draw):
|
current_set not in to_draw):
|
||||||
to_draw.append(current_set)
|
to_draw.append(current_set)
|
||||||
|
|
||||||
icons_file_name: str = "icons/icons.svg"
|
|
||||||
extractor: IconExtractor = IconExtractor(icons_file_name)
|
|
||||||
|
|
||||||
specified_ids: Set[str] = set()
|
specified_ids: Set[str] = set()
|
||||||
|
|
||||||
for icons_to_draw in to_draw: # type: List[str]
|
for icons_to_draw in to_draw: # type: List[str]
|
||||||
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.shapes.keys() - specified_ids)) + "."
|
||||||
)
|
)
|
||||||
|
|
||||||
draw_grid(
|
draw_grid(
|
||||||
|
|
|
@ -5,12 +5,10 @@ Author: Sergey Vartanov (me@enzet.ru).
|
||||||
"""
|
"""
|
||||||
import re
|
import re
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Any, Dict, Optional
|
from typing import Dict, Optional
|
||||||
from xml.dom.minidom import Document, Element, Node, parse
|
from xml.dom.minidom import Document, Element, Node, parse
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import svgwrite
|
|
||||||
from colour import Color
|
|
||||||
from svgwrite import Drawing
|
from svgwrite import Drawing
|
||||||
|
|
||||||
from roentgen import ui
|
from roentgen import ui
|
||||||
|
@ -55,56 +53,6 @@ class Shape:
|
||||||
d=self.path, transform=f"translate({shift[0]},{shift[1]})"
|
d=self.path, transform=f"translate({shift[0]},{shift[1]})"
|
||||||
)
|
)
|
||||||
|
|
||||||
def draw(
|
|
||||||
self, svg: svgwrite.Drawing, point: np.array, color: Color,
|
|
||||||
opacity: float = 1.0, tags: Dict[str, Any] = None, outline: bool = False
|
|
||||||
) -> None:
|
|
||||||
"""
|
|
||||||
Draw icon shape into SVG file.
|
|
||||||
|
|
||||||
:param svg: output SVG file
|
|
||||||
:param point: 2D position of the icon centre
|
|
||||||
: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)
|
|
||||||
|
|
||||||
|
|
||||||
def is_standard(id_: str):
|
|
||||||
"""
|
|
||||||
Check whether SVG object ID is standard Inkscape ID.
|
|
||||||
"""
|
|
||||||
matcher = re.match(STANDARD_INKSCAPE_ID, id_)
|
|
||||||
return matcher is not None
|
|
||||||
if id_ == "base":
|
|
||||||
return True
|
|
||||||
for prefix in [
|
|
||||||
]:
|
|
||||||
matcher = re.match(prefix + "\\d+", id_)
|
|
||||||
if matcher:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class IconExtractor:
|
class IconExtractor:
|
||||||
"""
|
"""
|
||||||
|
@ -118,10 +66,10 @@ class IconExtractor:
|
||||||
:param svg_file_name: input SVG file name with icons. File may contain
|
:param svg_file_name: input SVG file name with icons. File may contain
|
||||||
any other irrelevant graphics.
|
any other irrelevant graphics.
|
||||||
"""
|
"""
|
||||||
self.icons: Dict[str, Shape] = {}
|
self.shapes: Dict[str, Shape] = {}
|
||||||
|
|
||||||
with open(svg_file_name) as input_file:
|
with open(svg_file_name) as input_file:
|
||||||
content = parse(input_file) # type: Document
|
content: Document = parse(input_file)
|
||||||
for element in content.childNodes: # type: Element
|
for element in content.childNodes: # type: Element
|
||||||
if element.nodeName != "svg":
|
if element.nodeName != "svg":
|
||||||
continue
|
continue
|
||||||
|
@ -145,7 +93,7 @@ class IconExtractor:
|
||||||
return
|
return
|
||||||
|
|
||||||
id_: str = node.getAttribute("id")
|
id_: str = node.getAttribute("id")
|
||||||
if is_standard(id_):
|
if re.match(STANDARD_INKSCAPE_ID, id_) is not None:
|
||||||
return
|
return
|
||||||
|
|
||||||
if node.hasAttribute("d"):
|
if node.hasAttribute("d"):
|
||||||
|
@ -172,7 +120,7 @@ class IconExtractor:
|
||||||
name = child_node.childNodes[0].nodeValue
|
name = child_node.childNodes[0].nodeValue
|
||||||
break
|
break
|
||||||
|
|
||||||
self.icons[id_] = Shape(path, point, id_, name)
|
self.shapes[id_] = Shape(path, point, id_, name)
|
||||||
else:
|
else:
|
||||||
ui.error(f"not standard ID {id_}")
|
ui.error(f"not standard ID {id_}")
|
||||||
|
|
||||||
|
@ -182,8 +130,8 @@ class IconExtractor:
|
||||||
|
|
||||||
:param id_: string icon identifier
|
:param id_: string icon identifier
|
||||||
"""
|
"""
|
||||||
if id_ in self.icons:
|
if id_ in self.shapes:
|
||||||
return self.icons[id_], True
|
return self.shapes[id_], True
|
||||||
|
|
||||||
ui.error(f"no such icon ID {id_}")
|
ui.error(f"no such shape ID {id_}")
|
||||||
return self.icons[DEFAULT_SHAPE_ID], False
|
return self.shapes[DEFAULT_SHAPE_ID], False
|
||||||
|
|
|
@ -7,7 +7,7 @@ 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
|
from roentgen.scheme import Icon, ShapeSpecification
|
||||||
from roentgen.text import Label
|
from roentgen.text import Label
|
||||||
|
|
||||||
DEFAULT_FONT: str = "Roboto"
|
DEFAULT_FONT: str = "Roboto"
|
||||||
|
@ -71,17 +71,17 @@ class Point(Tagged):
|
||||||
Draw main shape for one node.
|
Draw main shape for one node.
|
||||||
"""
|
"""
|
||||||
if (
|
if (
|
||||||
self.icon.main_icon and
|
self.icon.main_icon[0].is_default() and
|
||||||
(not self.icon.main_icon[0].is_default() or
|
not self.icon.extra_icons
|
||||||
self.is_for_node)
|
|
||||||
):
|
):
|
||||||
position = self.point + np.array((0, self.y))
|
return
|
||||||
self.main_icon_painted: bool = self.draw_point_shape(
|
|
||||||
svg, self.icon.main_icon,
|
position = self.point + np.array((0, self.y))
|
||||||
position, self.icon.color, occupied,
|
self.main_icon_painted: bool = self.draw_point_shape(
|
||||||
tags=self.tags)
|
svg, self.icon.main_icon, position, occupied, tags=self.tags
|
||||||
if self.main_icon_painted:
|
)
|
||||||
self.y += 16
|
if self.main_icon_painted:
|
||||||
|
self.y += 16
|
||||||
|
|
||||||
def draw_extra_shapes(
|
def draw_extra_shapes(
|
||||||
self, svg: svgwrite.Drawing, occupied: Optional[Occupied] = None
|
self, svg: svgwrite.Drawing, occupied: Optional[Occupied] = None
|
||||||
|
@ -108,14 +108,14 @@ class Point(Tagged):
|
||||||
for shape_ids in self.icon.extra_icons:
|
for shape_ids in self.icon.extra_icons:
|
||||||
self.draw_point_shape(
|
self.draw_point_shape(
|
||||||
svg, shape_ids, self.point + np.array((left, self.y)),
|
svg, shape_ids, self.point + np.array((left, self.y)),
|
||||||
Color("#888888"), occupied)
|
occupied=occupied)
|
||||||
left += 16
|
left += 16
|
||||||
if self.icon.extra_icons:
|
if self.icon.extra_icons:
|
||||||
self.y += 16
|
self.y += 16
|
||||||
|
|
||||||
def draw_point_shape(
|
def draw_point_shape(
|
||||||
self, svg: svgwrite.Drawing, shapes: List[Shape], position,
|
self, svg: svgwrite.Drawing, shapes: List[ShapeSpecification], position,
|
||||||
fill: Color, occupied, tags: Optional[Dict[str, str]] = None
|
occupied, tags: Optional[Dict[str, str]] = None
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""
|
"""
|
||||||
Draw one combined icon and its outline.
|
Draw one combined icon and its outline.
|
||||||
|
@ -129,16 +129,13 @@ class Point(Tagged):
|
||||||
# Draw outlines.
|
# Draw outlines.
|
||||||
|
|
||||||
if self.draw_outline:
|
if self.draw_outline:
|
||||||
for icon in shapes: # type: Shape
|
for shape in shapes:
|
||||||
bright: bool = is_bright(fill)
|
shape.draw(svg, position, outline=True)
|
||||||
color: Color = Color("black") if bright else Color("white")
|
|
||||||
opacity: float = 0.7 if bright else 0.5
|
|
||||||
icon.draw(svg, position, color, opacity=opacity, outline=True)
|
|
||||||
|
|
||||||
# Draw icons.
|
# Draw icons.
|
||||||
|
|
||||||
for icon in shapes: # type: Shape
|
for shape in shapes: # type: ShapeSpecification
|
||||||
icon.draw(svg, position, fill, tags=tags)
|
shape.draw(svg, position, tags=tags)
|
||||||
|
|
||||||
if occupied:
|
if occupied:
|
||||||
overlap: int = occupied.overlap
|
overlap: int = occupied.overlap
|
||||||
|
|
|
@ -3,32 +3,102 @@ Röntgen drawing scheme.
|
||||||
|
|
||||||
Author: Sergey Vartanov (me@enzet.ru).
|
Author: Sergey Vartanov (me@enzet.ru).
|
||||||
"""
|
"""
|
||||||
import copy
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Any, Dict, List, Optional, Set, Tuple, Union
|
from typing import Any, Dict, List, Optional, Set, Tuple, Union
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import svgwrite
|
||||||
import yaml
|
import yaml
|
||||||
from colour import Color
|
from colour import Color
|
||||||
|
|
||||||
|
from roentgen.color import is_bright
|
||||||
from roentgen.icon import DEFAULT_SHAPE_ID, IconExtractor, Shape
|
from roentgen.icon import DEFAULT_SHAPE_ID, IconExtractor, Shape
|
||||||
from roentgen.text import Label, get_address, get_text
|
from roentgen.text import Label, get_address, get_text
|
||||||
|
|
||||||
DEFAULT_COLOR: Color = Color("#444444")
|
DEFAULT_COLOR: Color = Color("#444444")
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ShapeSpecification:
|
||||||
|
"""
|
||||||
|
Specification for shape as a part of an icon.
|
||||||
|
"""
|
||||||
|
|
||||||
|
shape: Shape
|
||||||
|
color: Color = DEFAULT_COLOR
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_structure(
|
||||||
|
cls, structure: Any, extractor: IconExtractor, scheme: "Scheme",
|
||||||
|
color: Color = DEFAULT_COLOR
|
||||||
|
) -> "ShapeSpecification":
|
||||||
|
"""
|
||||||
|
Parse shape specification from structure.
|
||||||
|
"""
|
||||||
|
shape: Shape
|
||||||
|
shape, _ = extractor.get_path(DEFAULT_SHAPE_ID)
|
||||||
|
color: Color = color
|
||||||
|
if isinstance(structure, str):
|
||||||
|
shape, _ = extractor.get_path(structure)
|
||||||
|
elif isinstance(structure, dict):
|
||||||
|
if "shape" in structure:
|
||||||
|
shape, _ = extractor.get_path(structure["shape"])
|
||||||
|
if "color" in structure:
|
||||||
|
color = scheme.get_color(structure["color"])
|
||||||
|
return cls(shape, color)
|
||||||
|
|
||||||
|
def is_default(self) -> bool:
|
||||||
|
"""
|
||||||
|
Check whether shape is default.
|
||||||
|
"""
|
||||||
|
return self.shape.id_ == DEFAULT_SHAPE_ID
|
||||||
|
|
||||||
|
def draw(
|
||||||
|
self, svg: svgwrite.Drawing, point: np.array,
|
||||||
|
tags: Dict[str, Any] = None, outline: bool = False
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Draw icon shape into SVG file.
|
||||||
|
|
||||||
|
:param svg: output SVG file
|
||||||
|
:param point: 2D position of the icon centre
|
||||||
|
: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.shape.get_path(svg, point)
|
||||||
|
path.update({"fill": self.color.hex})
|
||||||
|
if outline:
|
||||||
|
bright: bool = is_bright(self.color)
|
||||||
|
color: Color = Color("black") if bright else Color("white")
|
||||||
|
opacity: float = 0.7 if bright else 0.5
|
||||||
|
|
||||||
|
path.update({
|
||||||
|
"fill": color.hex,
|
||||||
|
"stroke": color.hex,
|
||||||
|
"stroke-width": 2.2,
|
||||||
|
"stroke-linejoin": "round",
|
||||||
|
"opacity": opacity,
|
||||||
|
})
|
||||||
|
if tags:
|
||||||
|
title: str = "\n".join(map(lambda x: x + ": " + tags[x], tags))
|
||||||
|
path.set_desc(title=title)
|
||||||
|
svg.add(path)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Icon:
|
class Icon:
|
||||||
"""
|
"""
|
||||||
Node representation: icons and color.
|
Node representation: icons and color.
|
||||||
"""
|
"""
|
||||||
main_icon: List[Shape] # list of icons
|
main_icon: List[ShapeSpecification] # list of shapes
|
||||||
extra_icons: List[List[Shape]] # list of lists of icons
|
extra_icons: List[List[ShapeSpecification]] # list of lists of shapes
|
||||||
color: Color # fill color of all icons
|
|
||||||
# tag keys that were processed to create icon set (other
|
# tag keys that were processed to create icon set (other
|
||||||
# tag keys should be displayed by text or ignored)
|
# tag keys should be displayed by text or ignored)
|
||||||
processed: Set[str]
|
processed: Set[str]
|
||||||
is_default: bool
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
@ -148,9 +218,7 @@ class Scheme:
|
||||||
try:
|
try:
|
||||||
return Color(color)
|
return Color(color)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
return DEFAULT_COLOR
|
||||||
|
|
||||||
return DEFAULT_COLOR
|
|
||||||
|
|
||||||
def is_no_drawable(self, key: str) -> bool:
|
def is_no_drawable(self, key: str) -> bool:
|
||||||
"""
|
"""
|
||||||
|
@ -199,72 +267,79 @@ class Scheme:
|
||||||
if tags_hash in self.cache:
|
if tags_hash in self.cache:
|
||||||
return self.cache[tags_hash]
|
return self.cache[tags_hash]
|
||||||
|
|
||||||
main_icon_id: Optional[List[str]] = None
|
main_icon: List[ShapeSpecification] = []
|
||||||
extra_icon_ids: List[List[str]] = []
|
extra_icons: List[List[ShapeSpecification]] = []
|
||||||
processed: Set[str] = set()
|
processed: Set[str] = set()
|
||||||
fill: Color = DEFAULT_COLOR
|
|
||||||
priority: int = 0
|
priority: int = 0
|
||||||
|
|
||||||
for index, matcher in enumerate(self.icons):
|
for index, matcher in enumerate(self.icons):
|
||||||
# type: (int, Dict[str, Any])
|
index: int
|
||||||
|
matcher: Dict[str, Any]
|
||||||
matched: bool = is_matched(matcher, tags)
|
matched: bool = is_matched(matcher, tags)
|
||||||
|
matcher_tags: Set[str] = matcher["tags"].keys()
|
||||||
if not matched:
|
if not matched:
|
||||||
continue
|
continue
|
||||||
priority = len(self.icons) - index
|
priority = len(self.icons) - index
|
||||||
if "draw" in matcher and not matcher["draw"]:
|
if "draw" in matcher and not matcher["draw"]:
|
||||||
processed |= set(matcher["tags"].keys())
|
processed |= set(matcher_tags)
|
||||||
if "icon" in matcher:
|
if "icon" in matcher:
|
||||||
main_icon_id = copy.deepcopy(matcher["icon"])
|
main_icon = [
|
||||||
processed |= set(matcher["tags"].keys())
|
ShapeSpecification.from_structure(x, icon_extractor, self)
|
||||||
|
for x in matcher["icon"]
|
||||||
|
]
|
||||||
|
processed |= set(matcher_tags)
|
||||||
if "over_icon" in matcher:
|
if "over_icon" in matcher:
|
||||||
if main_icon_id: # TODO: check main icon in under icons
|
if main_icon:
|
||||||
main_icon_id += matcher["over_icon"]
|
main_icon += [
|
||||||
for key in matcher["tags"].keys():
|
ShapeSpecification.from_structure(
|
||||||
|
x, icon_extractor, self
|
||||||
|
)
|
||||||
|
for x in matcher["over_icon"]
|
||||||
|
]
|
||||||
|
for key in matcher_tags:
|
||||||
processed.add(key)
|
processed.add(key)
|
||||||
if "add_icon" in matcher:
|
if "add_icon" in matcher:
|
||||||
extra_icon_ids += [matcher["add_icon"]]
|
extra_icons += [[
|
||||||
for key in matcher["tags"].keys():
|
ShapeSpecification.from_structure(
|
||||||
|
x, icon_extractor, self, Color("#888888")
|
||||||
|
)
|
||||||
|
for x in matcher["add_icon"]
|
||||||
|
]]
|
||||||
|
for key in matcher_tags:
|
||||||
processed.add(key)
|
processed.add(key)
|
||||||
if "color" in matcher:
|
if "color" in matcher:
|
||||||
fill = self.get_color(matcher["color"])
|
assert False
|
||||||
for key in matcher["tags"].keys():
|
if "set_main_color" in matcher:
|
||||||
processed.add(key)
|
for shape in main_icon:
|
||||||
|
shape.color = self.get_color(matcher["set_main_color"])
|
||||||
|
|
||||||
|
color: Optional[Color] = None
|
||||||
|
|
||||||
for tag_key in tags: # type: str
|
for tag_key in tags: # type: str
|
||||||
if (tag_key.endswith(":color") or
|
if (tag_key.endswith(":color") or
|
||||||
tag_key.endswith(":colour")):
|
tag_key.endswith(":colour")):
|
||||||
fill = self.get_color(tags[tag_key])
|
color = self.get_color(tags[tag_key])
|
||||||
processed.add(tag_key)
|
processed.add(tag_key)
|
||||||
|
|
||||||
for tag_key in tags: # type: str
|
for tag_key in tags: # type: str
|
||||||
if tag_key in ["color", "colour"]:
|
if tag_key in ["color", "colour"]:
|
||||||
fill = self.get_color(tags[tag_key])
|
color = self.get_color(tags[tag_key])
|
||||||
processed.add(tag_key)
|
processed.add(tag_key)
|
||||||
|
|
||||||
keys_left = list(filter(
|
if color:
|
||||||
lambda x: x not in processed and
|
for shape_specification in main_icon:
|
||||||
not self.is_no_drawable(x), tags.keys()
|
shape_specification.color = color
|
||||||
))
|
|
||||||
|
|
||||||
is_default: bool = False
|
keys_left = [
|
||||||
if not main_icon_id and not extra_icon_ids and keys_left:
|
x for x in tags.keys()
|
||||||
main_icon_id = [DEFAULT_SHAPE_ID]
|
if x not in processed and not self.is_no_drawable(x)
|
||||||
is_default = True
|
]
|
||||||
|
|
||||||
main_icon: List[Shape] = []
|
default_shape, _ = icon_extractor.get_path(DEFAULT_SHAPE_ID)
|
||||||
if main_icon_id:
|
if not main_icon:
|
||||||
main_icon = list(map(
|
main_icon = [ShapeSpecification(default_shape)]
|
||||||
lambda x: icon_extractor.get_path(x)[0], main_icon_id
|
|
||||||
))
|
|
||||||
|
|
||||||
extra_icons: List[List[Shape]] = []
|
returned: Icon = Icon(main_icon, extra_icons, processed)
|
||||||
for icon_id in extra_icon_ids:
|
|
||||||
extra_icons.append(list(map(
|
|
||||||
lambda x: icon_extractor.get_path(x)[0], icon_id)))
|
|
||||||
|
|
||||||
returned: Icon = Icon(
|
|
||||||
main_icon, extra_icons, fill, processed, is_default
|
|
||||||
)
|
|
||||||
self.cache[tags_hash] = returned, priority
|
self.cache[tags_hash] = returned, priority
|
||||||
|
|
||||||
return returned, priority
|
return returned, priority
|
||||||
|
@ -283,8 +358,9 @@ class Scheme:
|
||||||
priority = element["priority"]
|
priority = element["priority"]
|
||||||
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", "r1",
|
||||||
"r", "r1", "r2"]:
|
"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)
|
||||||
|
@ -374,6 +450,9 @@ class Scheme:
|
||||||
if k in tags:
|
if k in tags:
|
||||||
texts.append(Label(tags[k], Color("#444444")))
|
texts.append(Label(tags[k], Color("#444444")))
|
||||||
tags.pop(k)
|
tags.pop(k)
|
||||||
|
if "height" in tags:
|
||||||
|
texts.append(Label(f"↕ {tags['height']} m"))
|
||||||
|
tags.pop("height")
|
||||||
for tag in tags:
|
for tag in tags:
|
||||||
if self.is_writable(tag):
|
if self.is_writable(tag):
|
||||||
texts.append(Label(tags[tag]))
|
texts.append(Label(tags[tag]))
|
||||||
|
|
|
@ -141,31 +141,24 @@ node_icons:
|
||||||
icon: [electricity]
|
icon: [electricity]
|
||||||
# plant=*
|
# plant=*
|
||||||
- tags: {plant: christmas_trees}
|
- tags: {plant: christmas_trees}
|
||||||
icon: [christmas_tree]
|
icon: [{shape: christmas_tree, color: orchard_border_color}]
|
||||||
color: orchard_border_color
|
|
||||||
# produce=*
|
# produce=*
|
||||||
- tags: {produce: apple}
|
- tags: {produce: apple}
|
||||||
icon: [apple]
|
icon: [{shape: apple, color: orchard_border_color}]
|
||||||
color: orchard_border_color
|
|
||||||
- tags: {produce: christmas_trees}
|
- tags: {produce: christmas_trees}
|
||||||
icon: [christmas_tree]
|
icon: [{shape: christmas_tree, color: orchard_border_color}]
|
||||||
color: orchard_border_color
|
|
||||||
- tags: {produce: pear}
|
- tags: {produce: pear}
|
||||||
icon: [pear]
|
icon: [{shape: pear, color: orchard_border_color}]
|
||||||
color: orchard_border_color
|
|
||||||
# trees=*
|
# trees=*
|
||||||
- tags: {trees: apple_trees}
|
- tags: {trees: apple_trees}
|
||||||
icon: [apple]
|
icon: [{shape: apple, color: orchard_border_color}]
|
||||||
color: orchard_border_color
|
|
||||||
- tags: {trees: pear_trees}
|
- tags: {trees: pear_trees}
|
||||||
icon: [pear]
|
icon: [{shape: pear, color: orchard_border_color}]
|
||||||
color: orchard_border_color
|
|
||||||
|
|
||||||
# Bigger objects
|
# Bigger objects
|
||||||
|
|
||||||
- tags: {waterway: waterfall}
|
- tags: {waterway: waterfall}
|
||||||
icon: [waterfall]
|
icon: [{shape: waterfall, color: water_border_color}]
|
||||||
color: water_border_color
|
|
||||||
- tags: {natural: cliff}
|
- tags: {natural: cliff}
|
||||||
icon: [cliff]
|
icon: [cliff]
|
||||||
- tags: {natural: peak}
|
- tags: {natural: peak}
|
||||||
|
@ -361,19 +354,15 @@ node_icons:
|
||||||
# Emergency
|
# Emergency
|
||||||
|
|
||||||
- tags: {emergency: defibrillator}
|
- tags: {emergency: defibrillator}
|
||||||
icon: [defibrillator]
|
icon: [{shape: defibrillator, color: emergency_color}]
|
||||||
color: emergency_color
|
|
||||||
- tags: {emergency: fire_extinguisher}
|
- tags: {emergency: fire_extinguisher}
|
||||||
icon: [fire_extinguisher]
|
icon: [{shape: fire_extinguisher, color: emergency_color}]
|
||||||
color: emergency_color
|
|
||||||
- tags: {emergency: fire_hydrant}
|
- tags: {emergency: fire_hydrant}
|
||||||
icon: [fire_hydrant]
|
icon: [fire_hydrant]
|
||||||
- tags: {emergency: life_ring}
|
- tags: {emergency: life_ring}
|
||||||
icon: [life_ring]
|
icon: [{shape: life_ring, color: emergency_color}]
|
||||||
color: emergency_color
|
|
||||||
- tags: {emergency: phone}
|
- tags: {emergency: phone}
|
||||||
icon: [sos_phone]
|
icon: [{shape: sos_phone, color: emergency_color}]
|
||||||
color: emergency_color
|
|
||||||
|
|
||||||
# Transport-important middle objects
|
# Transport-important middle objects
|
||||||
|
|
||||||
|
@ -628,8 +617,7 @@ node_icons:
|
||||||
- tags: {amenity: clock}
|
- tags: {amenity: clock}
|
||||||
icon: [clock]
|
icon: [clock]
|
||||||
- tags: {amenity: fountain}
|
- tags: {amenity: fountain}
|
||||||
icon: [fountain]
|
icon: [{shape: fountain, color: water_border_color}]
|
||||||
color: water_border_color
|
|
||||||
- tags: {amenity: waste_basket}
|
- tags: {amenity: waste_basket}
|
||||||
icon: [waste_basket]
|
icon: [waste_basket]
|
||||||
- tags: {highway: street_lamp}
|
- tags: {highway: street_lamp}
|
||||||
|
@ -666,50 +654,38 @@ node_icons:
|
||||||
icon: [lowered_kerb]
|
icon: [lowered_kerb]
|
||||||
# Trees
|
# Trees
|
||||||
- tags: {natural: tree}
|
- tags: {natural: tree}
|
||||||
icon: [tree]
|
icon: [{shape: tree, color: tree_color}]
|
||||||
color: tree_color
|
|
||||||
- tags: {leaf_type: broadleaved}
|
- tags: {leaf_type: broadleaved}
|
||||||
icon: [tree_with_leaf]
|
icon: [{shape: tree_with_leaf, color: tree_color}]
|
||||||
color: tree_color
|
|
||||||
- tags: {leaf_type: needleleaved}
|
- tags: {leaf_type: needleleaved}
|
||||||
icon: [needleleaved_tree]
|
icon: [{shape: needleleaved_tree, color: tree_color}]
|
||||||
color: tree_color
|
|
||||||
- tags: {leaf_type: palm}
|
- tags: {leaf_type: palm}
|
||||||
icon: [palm]
|
icon: [{shape: palm, color: tree_color}]
|
||||||
color: tree_color
|
|
||||||
- tags: {natural: tree, leaf_type: broadleaved}
|
- tags: {natural: tree, leaf_type: broadleaved}
|
||||||
icon: [tree_with_leaf]
|
icon: [{shape: tree_with_leaf, color: tree_color}]
|
||||||
color: tree_color
|
|
||||||
- tags: {natural: tree, leaf_type: needleleaved}
|
- tags: {natural: tree, leaf_type: needleleaved}
|
||||||
icon: [needleleaved_tree]
|
icon: [{shape: needleleaved_tree, color: tree_color}]
|
||||||
color: tree_color
|
|
||||||
- tags: {natural: tree, leaf_type: palm}
|
- tags: {natural: tree, leaf_type: palm}
|
||||||
icon: [palm]
|
icon: [{shape: palm, color: tree_color}]
|
||||||
color: tree_color
|
|
||||||
- tags: {natural: tree, type: conifer}
|
- tags: {natural: tree, type: conifer}
|
||||||
icon: [needleleaved_tree]
|
icon: [{shape: needleleaved_tree, color: tree_color}]
|
||||||
color: tree_color
|
|
||||||
- tags: {leaf_cycle: deciduous}
|
- tags: {leaf_cycle: deciduous}
|
||||||
color: decidious_color
|
set_main_color: decidious_color
|
||||||
- tags: {leaf_cycle: evergreen}
|
- tags: {leaf_cycle: evergreen}
|
||||||
color: evergreen_color
|
set_main_color: evergreen_color
|
||||||
- tags: {natural: tree, leaf_cycle: deciduous}
|
- tags: {natural: tree, leaf_cycle: deciduous}
|
||||||
color: decidious_color
|
set_main_color: decidious_color
|
||||||
- tags: {natural: tree, leaf_cycle: evergreen}
|
- tags: {natural: tree, leaf_cycle: evergreen}
|
||||||
color: evergreen_color
|
set_main_color: evergreen_color
|
||||||
- tags: {natural: bush}
|
- tags: {natural: bush}
|
||||||
icon: [bush]
|
icon: [{shape: bush, color: tree_color}]
|
||||||
color: tree_color
|
|
||||||
# Tree genus
|
# Tree genus
|
||||||
- tags: {natural: tree, genus: Betula}
|
- tags: {natural: tree, genus: Betula}
|
||||||
icon: [betula]
|
icon: [{shape: betula, color: tree_color}]
|
||||||
color: tree_color
|
|
||||||
- tags: {natural: tree, "genus:en": Birch}
|
- tags: {natural: tree, "genus:en": Birch}
|
||||||
icon: [betula]
|
icon: [{shape: betula, color: tree_color}]
|
||||||
color: tree_color
|
|
||||||
- tags: {natural: tree, "genus:ru": Берёза}
|
- tags: {natural: tree, "genus:ru": Берёза}
|
||||||
icon: [betula]
|
icon: [{shape: betula, color: tree_color}]
|
||||||
color: tree_color
|
|
||||||
|
|
||||||
- tags: {railway: buffer_stop}
|
- tags: {railway: buffer_stop}
|
||||||
icon: [buffer_stop]
|
icon: [buffer_stop]
|
||||||
|
|