mirror of
https://github.com/enzet/map-machine.git
synced 2025-05-01 03:07:41 +02:00
185 lines
6.1 KiB
Python
185 lines
6.1 KiB
Python
"""
|
|
Röntgen drawing scheme.
|
|
|
|
Author: Sergey Vartanov (me@enzet.ru).
|
|
"""
|
|
import copy
|
|
import yaml
|
|
|
|
from typing import Any, Dict, List, Optional, Set
|
|
|
|
from roentgen.extract_icon import DEFAULT_SHAPE_ID
|
|
|
|
DEFAULT_COLOR: str = "444444"
|
|
|
|
|
|
class IconSet:
|
|
"""
|
|
Node representation: icons and color.
|
|
"""
|
|
def __init__(
|
|
self, icons: List[List[str]], color: str, processed: Set[str],
|
|
is_default: bool):
|
|
"""
|
|
:param icons: list of lists of shape identifiers
|
|
:param color: fill color of all icons
|
|
:param processed: tag keys that were processed to create icon set (other
|
|
tag keys should be displayed by text or ignored)
|
|
"""
|
|
self.icons: List[List[str]] = icons
|
|
self.color: str = color
|
|
self.processed: Set[str] = processed
|
|
self.is_default = is_default
|
|
|
|
|
|
class Scheme:
|
|
"""
|
|
Map style.
|
|
|
|
Specifies map colors and rules to draw icons for OpenStreetMap tags.
|
|
"""
|
|
def __init__(self, file_name: str, color_file_name: str):
|
|
"""
|
|
:param file_name: scheme file name with tags, colors, and tag key
|
|
specification
|
|
:param color_file_name: additional color scheme
|
|
"""
|
|
content: Dict[str, Any] = \
|
|
yaml.load(open(file_name).read(), Loader=yaml.FullLoader)
|
|
|
|
self.tags: List[Dict[str, Any]] = content["tags"]
|
|
self.ways: List[Dict[str, Any]] = content["ways"]
|
|
|
|
self.colors: Dict[str, str] = content["colors"]
|
|
w3c_colors: Dict[str, str] = \
|
|
yaml.load(open(color_file_name), Loader=yaml.FullLoader)
|
|
|
|
self.colors.update(w3c_colors)
|
|
|
|
self.tags_to_write: List[str] = content["tags_to_write"]
|
|
self.prefix_to_write: List[str] = content["prefix_to_write"]
|
|
self.tags_to_skip: List[str] = content["tags_to_skip"]
|
|
self.prefix_to_skip: List[str] = content["prefix_to_skip"]
|
|
|
|
# Storage for created icon sets.
|
|
self.cache: Dict[str, IconSet] = {}
|
|
|
|
def get_color(self, color: str) -> str:
|
|
"""
|
|
Return color if the color is in scheme, otherwise return default color.
|
|
|
|
:return: 6-digit color specification without "#"
|
|
"""
|
|
if color in self.colors:
|
|
return self.colors[color]
|
|
if color.startswith("#"):
|
|
return color[1:]
|
|
|
|
print(f"No color {color}.")
|
|
|
|
return DEFAULT_COLOR
|
|
|
|
def is_no_drawable(self, key: str) -> bool:
|
|
"""
|
|
Return true if key is specified as no drawable (should not be
|
|
represented on the map as icon set or as text) by the scheme.
|
|
|
|
:param key: OpenStreetMap tag key
|
|
"""
|
|
if key in self.tags_to_write or key in self.tags_to_skip:
|
|
return True
|
|
for prefix in self.prefix_to_write + self.prefix_to_skip: # type: str
|
|
if key[:len(prefix) + 1] == prefix + ":":
|
|
return True
|
|
return False
|
|
|
|
def is_writable(self, key: str) -> bool:
|
|
"""
|
|
Return true if key is specified as writable (should be represented on
|
|
the map as text) by the scheme.
|
|
|
|
:param key: OpenStreetMap tag key
|
|
"""
|
|
if key in self.tags_to_skip:
|
|
return False
|
|
if key in self.tags_to_write:
|
|
return True
|
|
for prefix in self.prefix_to_write:
|
|
if key[:len(prefix) + 1] == prefix + ":":
|
|
return True
|
|
return False
|
|
|
|
def get_icon(self, tags: Dict[str, Any]) -> IconSet:
|
|
"""
|
|
Construct icon set.
|
|
|
|
:param tags: OpenStreetMap element tags dictionary
|
|
"""
|
|
tags_hash: str = \
|
|
",".join(tags.keys()) + ":" + \
|
|
",".join(map(lambda x: str(x), tags.values()))
|
|
|
|
if tags_hash in self.cache:
|
|
return self.cache[tags_hash]
|
|
|
|
main_icon: Optional[List[str]] = None
|
|
extra_icons: List[List[str]] = []
|
|
processed = set()
|
|
fill = DEFAULT_COLOR
|
|
|
|
for matcher in self.tags:
|
|
matched = True
|
|
for key in matcher["tags"]:
|
|
if key not in tags:
|
|
matched = False
|
|
break
|
|
if matcher["tags"][key] != "*" and \
|
|
matcher["tags"][key] != tags[key]:
|
|
matched = False
|
|
break
|
|
if "no_tags" in matcher:
|
|
for no_tag in matcher["no_tags"]:
|
|
if no_tag in tags.keys():
|
|
matched = False
|
|
break
|
|
if matched:
|
|
if "draw" in matcher and not matcher["draw"]:
|
|
processed |= set(matcher["tags"].keys())
|
|
if "icon" in matcher:
|
|
main_icon = copy.deepcopy(matcher["icon"])
|
|
processed |= set(matcher["tags"].keys())
|
|
if "over_icon" in matcher:
|
|
if main_icon: # TODO: check main icon in under icons
|
|
main_icon += matcher["over_icon"]
|
|
for key in matcher["tags"].keys():
|
|
processed.add(key)
|
|
if "add_icon" in matcher:
|
|
extra_icons += [matcher["add_icon"]]
|
|
for key in matcher["tags"].keys():
|
|
processed.add(key)
|
|
if "color" in matcher:
|
|
fill = self.colors[matcher["color"]]
|
|
for key in matcher["tags"].keys():
|
|
processed.add(key)
|
|
|
|
for tag_key in tags: # type: str
|
|
if tag_key in ["color", "colour"] or tag_key.endswith(":color") or \
|
|
tag_key.endswith(":colour"):
|
|
fill = self.get_color(tags[tag_key])
|
|
processed.add(tag_key)
|
|
|
|
if main_icon:
|
|
result_set: List[List[str]] = [main_icon] + extra_icons
|
|
else:
|
|
result_set: List[List[str]] = extra_icons
|
|
|
|
is_default: bool = False
|
|
if not result_set and tags:
|
|
result_set = [[DEFAULT_SHAPE_ID]]
|
|
is_default = True
|
|
|
|
returned: IconSet = IconSet(result_set, fill, processed, is_default)
|
|
|
|
self.cache[tags_hash] = returned
|
|
|
|
return returned
|