mirror of
https://github.com/enzet/map-machine.git
synced 2025-05-04 04:36:54 +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.
|
MapCSS scheme creation.
|
||||||
"""
|
"""
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, List
|
from typing import Dict, List, Optional
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from colour import Color
|
from colour import Color
|
||||||
|
|
||||||
from roentgen.grid import IconCollection
|
from roentgen.grid import IconCollection
|
||||||
from roentgen.icon import ShapeExtractor
|
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:
|
class MapCSSWriter:
|
||||||
"""
|
def __init__(
|
||||||
Construct icon selectors for MapCSS 0.2 scheme.
|
self,
|
||||||
"""
|
scheme: Scheme,
|
||||||
selectors: Dict[str, List[str]] = {}
|
icon_directory_name: str,
|
||||||
for matcher in scheme.node_matchers:
|
add_icons_for_lifecycle: bool,
|
||||||
if matcher.shapes and not matcher.location_restrictions:
|
):
|
||||||
# TODO: support location restrictions
|
self.add_icons_for_lifecycle = add_icons_for_lifecycle
|
||||||
selectors[matcher.get_mapcss_selector()] = [
|
self.icon_directory_name = icon_directory_name
|
||||||
(x if isinstance(x, str) else x["shape"])
|
|
||||||
for x in matcher.shapes
|
|
||||||
]
|
|
||||||
|
|
||||||
s: str = ""
|
self.matchers: Dict[Matcher, List[str]] = {}
|
||||||
for selector in selectors:
|
for matcher in scheme.node_matchers:
|
||||||
for target in ["node", "area"]:
|
if matcher.shapes and not matcher.location_restrictions:
|
||||||
s += (
|
self.matchers[matcher] = [
|
||||||
target + selector + " {\n"
|
(x if isinstance(x, str) else x["shape"])
|
||||||
f' icon-image: "{icon_directory_name}/'
|
for x in matcher.shapes
|
||||||
+ "___".join(selectors[selector])
|
]
|
||||||
+ '.svg";\n}\n'
|
|
||||||
)
|
def add_selector(
|
||||||
return s
|
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:
|
def write_mapcss() -> None:
|
||||||
|
@ -58,12 +99,10 @@ def write_mapcss() -> None:
|
||||||
collection.draw_icons(
|
collection.draw_icons(
|
||||||
icons_with_outline_path, color=Color("black"), outline=True
|
icons_with_outline_path, color=Color("black"), outline=True
|
||||||
)
|
)
|
||||||
with Path("data/roentgen_icons_part.mapcss").open() as input_file:
|
mapcss_writer: MapCSSWriter = MapCSSWriter(
|
||||||
header = input_file.read()
|
scheme, icon_directory_name, True
|
||||||
|
)
|
||||||
with (directory / "roentgen_icons.mapcss").open("w+") as output_file:
|
with (directory / "roentgen_icons.mapcss").open("w+") as output_file:
|
||||||
output_file.write(header)
|
mapcss_writer.write(output_file)
|
||||||
output_file.write("\n")
|
|
||||||
output_file.write(construct_selectors(scheme, icon_directory_name))
|
|
||||||
|
|
||||||
logging.info(f"MapCSS 0.2 scheme is written to {directory}.")
|
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$")
|
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]:
|
def parse_float(string: str) -> Optional[float]:
|
||||||
"""
|
"""
|
||||||
Parse string representation of a float or integer value.
|
Parse string representation of a float or integer value.
|
||||||
|
|
|
@ -69,6 +69,17 @@ def is_matched_tag(
|
||||||
return MatchingType.NOT_MATCHED
|
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:
|
class Matcher:
|
||||||
"""
|
"""
|
||||||
Tag matching.
|
Tag matching.
|
||||||
|
@ -120,21 +131,13 @@ class Matcher:
|
||||||
|
|
||||||
return matched
|
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.
|
Construct MapCSS 0.2 selector from the node matcher.
|
||||||
|
|
||||||
See https://wiki.openstreetmap.org/wiki/MapCSS/0.2
|
See https://wiki.openstreetmap.org/wiki/MapCSS/0.2
|
||||||
"""
|
"""
|
||||||
def get_selector(key: str, value: str) -> str:
|
return "".join([get_selector(x, y, prefix) for (x, y) in self.tags.items()])
|
||||||
"""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()])
|
|
||||||
|
|
||||||
|
|
||||||
class NodeMatcher(Matcher):
|
class NodeMatcher(Matcher):
|
||||||
|
|
Loading…
Add table
Reference in a new issue