mirror of
https://github.com/enzet/map-machine.git
synced 2025-05-02 19:56:39 +02:00
Issue #63: add ghost icons.
Add keys with lifecycle prefixes to MapCSS with ghost icons (icons with low opacity).
This commit is contained in:
parent
071164efe0
commit
afaeb6f37a
3 changed files with 94 additions and 40 deletions
|
@ -2,39 +2,80 @@
|
|||
MapCSS scheme creation.
|
||||
"""
|
||||
from pathlib import Path
|
||||
from typing import Dict, List
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
import logging
|
||||
from colour import Color
|
||||
|
||||
from roentgen.grid import IconCollection
|
||||
from roentgen.icon import ShapeExtractor
|
||||
from roentgen.scheme import Scheme
|
||||
from roentgen.osm_reader import STAGES_OF_DECAY
|
||||
from roentgen.scheme import Scheme, Matcher
|
||||
|
||||
|
||||
def construct_selectors(scheme: Scheme, icon_directory_name: str) -> str:
|
||||
"""
|
||||
Construct icon selectors for MapCSS 0.2 scheme.
|
||||
"""
|
||||
selectors: Dict[str, List[str]] = {}
|
||||
for matcher in scheme.node_matchers:
|
||||
if matcher.shapes and not matcher.location_restrictions:
|
||||
# TODO: support location restrictions
|
||||
selectors[matcher.get_mapcss_selector()] = [
|
||||
(x if isinstance(x, str) else x["shape"])
|
||||
for x in matcher.shapes
|
||||
]
|
||||
class MapCSSWriter:
|
||||
def __init__(
|
||||
self,
|
||||
scheme: Scheme,
|
||||
icon_directory_name: str,
|
||||
add_icons_for_lifecycle: bool,
|
||||
):
|
||||
self.add_icons_for_lifecycle = add_icons_for_lifecycle
|
||||
self.icon_directory_name = icon_directory_name
|
||||
|
||||
s: str = ""
|
||||
for selector in selectors:
|
||||
for target in ["node", "area"]:
|
||||
s += (
|
||||
target + selector + " {\n"
|
||||
f' icon-image: "{icon_directory_name}/'
|
||||
+ "___".join(selectors[selector])
|
||||
+ '.svg";\n}\n'
|
||||
)
|
||||
return s
|
||||
self.matchers: Dict[Matcher, List[str]] = {}
|
||||
for matcher in scheme.node_matchers:
|
||||
if matcher.shapes and not matcher.location_restrictions:
|
||||
self.matchers[matcher] = [
|
||||
(x if isinstance(x, str) else x["shape"])
|
||||
for x in matcher.shapes
|
||||
]
|
||||
|
||||
def add_selector(
|
||||
self,
|
||||
target: str,
|
||||
matcher: Matcher,
|
||||
prefix: str = "",
|
||||
opacity: Optional[float] = None,
|
||||
) -> str:
|
||||
selector = (
|
||||
target + matcher.get_mapcss_selector(prefix) + " {\n"
|
||||
f' icon-image: "{self.icon_directory_name}/'
|
||||
+ "___".join(self.matchers[matcher])
|
||||
+ '.svg";\n'
|
||||
)
|
||||
if opacity is not None:
|
||||
selector += f" icon-opacity: {opacity:.2f};\n"
|
||||
selector += "}\n"
|
||||
return selector
|
||||
|
||||
def write(self, output_file) -> None:
|
||||
"""
|
||||
Construct icon selectors for MapCSS 0.2 scheme.
|
||||
"""
|
||||
with Path("data/roentgen_icons_part.mapcss").open() as input_file:
|
||||
output_file.write(input_file.read())
|
||||
|
||||
output_file.write("\n")
|
||||
|
||||
for matcher in self.matchers:
|
||||
for target in ["node", "area"]:
|
||||
output_file.write(self.add_selector(target, matcher))
|
||||
|
||||
if not self.add_icons_for_lifecycle:
|
||||
return
|
||||
|
||||
for index, stage_of_decay in enumerate(STAGES_OF_DECAY):
|
||||
opacity: float = 0.6 - 0.4 * index / (len(STAGES_OF_DECAY) - 1)
|
||||
for matcher in self.matchers:
|
||||
if len(matcher.tags) > 1:
|
||||
continue
|
||||
for target in ["node", "area"]:
|
||||
output_file.write(
|
||||
self.add_selector(
|
||||
target, matcher, stage_of_decay, opacity
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def write_mapcss() -> None:
|
||||
|
@ -58,12 +99,10 @@ def write_mapcss() -> None:
|
|||
collection.draw_icons(
|
||||
icons_with_outline_path, color=Color("black"), outline=True
|
||||
)
|
||||
with Path("data/roentgen_icons_part.mapcss").open() as input_file:
|
||||
header = input_file.read()
|
||||
|
||||
mapcss_writer: MapCSSWriter = MapCSSWriter(
|
||||
scheme, icon_directory_name, True
|
||||
)
|
||||
with (directory / "roentgen_icons.mapcss").open("w+") as output_file:
|
||||
output_file.write(header)
|
||||
output_file.write("\n")
|
||||
output_file.write(construct_selectors(scheme, icon_directory_name))
|
||||
mapcss_writer.write(output_file)
|
||||
|
||||
logging.info(f"MapCSS 0.2 scheme is written to {directory}.")
|
||||
|
|
|
@ -23,6 +23,18 @@ KILOMETERS_PATTERN = re.compile("^(?P<value>\\d*\\.?\\d*)\\s*km$")
|
|||
MILES_PATTERN = re.compile("^(?P<value>\\d*\\.?\\d*)\\s*mi$")
|
||||
|
||||
|
||||
STAGES_OF_DECAY: List[str] = [
|
||||
"disused",
|
||||
"abandoned",
|
||||
"ruins",
|
||||
"demolished",
|
||||
"removed",
|
||||
"razed",
|
||||
"destroyed",
|
||||
"was", # is not actually a stage of decay
|
||||
]
|
||||
|
||||
|
||||
def parse_float(string: str) -> Optional[float]:
|
||||
"""
|
||||
Parse string representation of a float or integer value.
|
||||
|
|
|
@ -69,6 +69,17 @@ def is_matched_tag(
|
|||
return MatchingType.NOT_MATCHED
|
||||
|
||||
|
||||
def get_selector(key: str, value: str, prefix: str = "") -> str:
|
||||
"""Get MapCSS 0.2 selector for one key."""
|
||||
if prefix:
|
||||
key = f"{prefix}:{key}"
|
||||
if value == "*":
|
||||
return f"[{key}]"
|
||||
if '"' in value:
|
||||
return f"[{key}='{value}']"
|
||||
return f'[{key}="{value}"]'
|
||||
|
||||
|
||||
class Matcher:
|
||||
"""
|
||||
Tag matching.
|
||||
|
@ -120,21 +131,13 @@ class Matcher:
|
|||
|
||||
return matched
|
||||
|
||||
def get_mapcss_selector(self) -> str:
|
||||
def get_mapcss_selector(self, prefix: str = "") -> str:
|
||||
"""
|
||||
Construct MapCSS 0.2 selector from the node matcher.
|
||||
|
||||
See https://wiki.openstreetmap.org/wiki/MapCSS/0.2
|
||||
"""
|
||||
def get_selector(key: str, value: str) -> str:
|
||||
"""Get MapCSS 0.2 selector for one key."""
|
||||
if value == "*":
|
||||
return f"[{key}]"
|
||||
if '"' in value:
|
||||
return f"[{key}='{value}']"
|
||||
return f'[{key}="{value}"]'
|
||||
|
||||
return "".join([get_selector(x, y) for (x, y) in self.tags.items()])
|
||||
return "".join([get_selector(x, y, prefix) for (x, y) in self.tags.items()])
|
||||
|
||||
|
||||
class NodeMatcher(Matcher):
|
||||
|
|
Loading…
Add table
Reference in a new issue