mirror of
https://github.com/enzet/map-machine.git
synced 2025-06-02 02:41:57 +02:00
Refactor and add documentation.
This commit is contained in:
parent
d2813da063
commit
010368dfea
10 changed files with 24 additions and 3 deletions
3
map_machine/doc/__init__.py
Normal file
3
map_machine/doc/__init__.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
"""
|
||||
Documentation utilities.
|
||||
"""
|
317
map_machine/doc/moire_manager.py
Normal file
317
map_machine/doc/moire_manager.py
Normal file
|
@ -0,0 +1,317 @@
|
|||
"""
|
||||
Moire markup extension for Map Machine.
|
||||
"""
|
||||
import argparse
|
||||
from abc import ABC
|
||||
from pathlib import Path
|
||||
from typing import Any, Union
|
||||
|
||||
from moire.default import Default, DefaultHTML, DefaultMarkdown, DefaultWiki
|
||||
from moire.moire import Tag
|
||||
|
||||
from map_machine.ui import cli
|
||||
from map_machine.pictogram.icon import ShapeExtractor
|
||||
from map_machine.ui.cli import COMMAND_LINES
|
||||
from map_machine.workspace import workspace
|
||||
|
||||
__author__ = "Sergey Vartanov"
|
||||
__email__ = "me@enzet.ru"
|
||||
|
||||
Arguments = list[Any]
|
||||
Code = Union[str, Tag, list]
|
||||
|
||||
PREFIX: str = "https://wiki.openstreetmap.org/wiki/"
|
||||
|
||||
|
||||
def parse_text(text: str, margins: str, tag_id: str) -> Code:
|
||||
"""Parse formal arguments."""
|
||||
word: str = ""
|
||||
result: Code = []
|
||||
inside: bool = False
|
||||
|
||||
for character in text:
|
||||
if character not in margins:
|
||||
word += character
|
||||
continue
|
||||
if word:
|
||||
result.append(Tag(tag_id, [word]) if inside else word)
|
||||
word = ""
|
||||
inside = not inside
|
||||
|
||||
if word:
|
||||
result.append(word)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class ArgumentParser(argparse.ArgumentParser):
|
||||
"""
|
||||
Argument parser that stores arguments and creates help in Moire markup.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs) -> None:
|
||||
self.arguments: list[dict[str, Any]] = []
|
||||
super(ArgumentParser, self).__init__(*args, **kwargs)
|
||||
|
||||
def add_argument(self, *args, **kwargs) -> None:
|
||||
"""Just store argument with options."""
|
||||
super(ArgumentParser, self).add_argument(*args, **kwargs)
|
||||
argument: dict[str, Any] = {"arguments": [x for x in args]}
|
||||
|
||||
for key in kwargs:
|
||||
argument[key] = kwargs[key]
|
||||
|
||||
self.arguments.append(argument)
|
||||
|
||||
def get_moire_help(self) -> Tag:
|
||||
"""
|
||||
Return Moire table with "Option" and "Description" columns filled with
|
||||
arguments.
|
||||
"""
|
||||
table: Code = [[["Option"], ["Description"]]]
|
||||
|
||||
for option in self.arguments:
|
||||
if option["arguments"][0] == "-h":
|
||||
continue
|
||||
|
||||
array: Code = [
|
||||
[Tag("no_wrap", [Tag("m", [x])]), ", "]
|
||||
for x in option["arguments"]
|
||||
]
|
||||
cell: Code = [x for y in array for x in y][:-1]
|
||||
if "metavar" in option:
|
||||
cell += [
|
||||
" ",
|
||||
Tag("m", [parse_text(option["metavar"], "<>", "formal")]),
|
||||
]
|
||||
row: Code = [cell]
|
||||
|
||||
if "help" in option:
|
||||
help_value: list = parse_text(option["help"], "`", "m")
|
||||
if (
|
||||
"default" in option
|
||||
and option["default"]
|
||||
and option["default"] != "==SUPPRESS=="
|
||||
):
|
||||
if (
|
||||
"action" in option
|
||||
and option["action"] == argparse.BooleanOptionalAction
|
||||
):
|
||||
if option["default"] is True:
|
||||
help_value += [", set by default"]
|
||||
elif option["default"] is False:
|
||||
help_value += [", not set by default"]
|
||||
else:
|
||||
default_value: Code = Tag("m", [str(option["default"])])
|
||||
if "type" in option and option["type"] in [int, float]:
|
||||
default_value = str(option["default"])
|
||||
help_value += [", default value: ", default_value]
|
||||
row.append(help_value)
|
||||
else:
|
||||
row.append([])
|
||||
|
||||
table.append(row)
|
||||
|
||||
return Tag("table", table)
|
||||
|
||||
|
||||
class MapMachineMoire(Default, ABC):
|
||||
"""
|
||||
Moire extension stub for Map Machine.
|
||||
"""
|
||||
|
||||
def osm(self, args: Arguments) -> str:
|
||||
"""OSM tag key or key–value pair of tag."""
|
||||
spec: str = self.clear(args[0])
|
||||
if "=" in spec:
|
||||
key, tag = spec.split("=")
|
||||
return (
|
||||
self.get_ref_(f"{PREFIX}Key:{key}", self.m([key]))
|
||||
+ "="
|
||||
+ self.get_ref_(f"{PREFIX}Tag:{key}={tag}", self.m([tag]))
|
||||
)
|
||||
else:
|
||||
return self.get_ref_(f"{PREFIX}Key:{spec}", self.m([spec]))
|
||||
|
||||
def color(self, args: Arguments) -> str:
|
||||
"""Simple color sample."""
|
||||
raise NotImplementedError("color")
|
||||
|
||||
def page_icon(self, args: Arguments) -> str:
|
||||
"""HTML page icon."""
|
||||
return ""
|
||||
|
||||
def command(self, args: Arguments) -> str:
|
||||
"""Bash command from integration tests."""
|
||||
return "map-machine " + " ".join(COMMAND_LINES[self.clear(args[0])])
|
||||
|
||||
def icon(self, args: Arguments) -> str:
|
||||
"""Image with Röntgen icon."""
|
||||
raise NotImplementedError("icon")
|
||||
|
||||
def options(self, args: Arguments) -> str:
|
||||
"""Table with option descriptions."""
|
||||
parser: ArgumentParser = ArgumentParser()
|
||||
command: str = self.clear(args[0])
|
||||
if command == "render":
|
||||
cli.add_render_arguments(parser)
|
||||
elif command == "server":
|
||||
cli.add_server_arguments(parser)
|
||||
elif command == "tile":
|
||||
cli.add_tile_arguments(parser)
|
||||
elif command == "map":
|
||||
cli.add_map_arguments(parser)
|
||||
elif command == "element":
|
||||
cli.add_element_arguments(parser)
|
||||
elif command == "mapcss":
|
||||
cli.add_mapcss_arguments(parser)
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
"no separate function for parser creation"
|
||||
)
|
||||
return self.parse(parser.get_moire_help())
|
||||
|
||||
def kbd(self, args: Arguments) -> str:
|
||||
"""Keyboard key."""
|
||||
return self.m(args)
|
||||
|
||||
def no_wrap(self, args: Arguments) -> str:
|
||||
"""Do not wrap text at white spaces."""
|
||||
return self.parse(args[0])
|
||||
|
||||
|
||||
class MapMachineHTML(MapMachineMoire, DefaultHTML):
|
||||
"""
|
||||
Simple HTML.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self.images: dict = {}
|
||||
|
||||
def table(self, arg: Arguments) -> str:
|
||||
"""Simple table. First row is treated as header."""
|
||||
content: str = ""
|
||||
cell: str = "".join(
|
||||
["<th>" + self.parse(td, in_block=True) + "</th>" for td in arg[0]]
|
||||
)
|
||||
content += f"<tr>{cell}</tr>"
|
||||
for tr in arg[1:]:
|
||||
cell: str = "".join(
|
||||
["<td>" + self.parse(td, in_block=True) + "</td>" for td in tr]
|
||||
)
|
||||
content += f"<tr>{cell}</tr>"
|
||||
return f"<table>{content}</table>"
|
||||
|
||||
def color(self, args: Arguments) -> str:
|
||||
"""Simple color sample."""
|
||||
return (
|
||||
f'<span class="color" '
|
||||
f'style="background-color: {self.clear(args[0])};"></span>'
|
||||
)
|
||||
|
||||
def icon(self, args: Arguments) -> str:
|
||||
"""Image with Röntgen icon."""
|
||||
size: str = self.clear(args[1]) if len(args) > 1 else 16
|
||||
return (
|
||||
f'<img class="icon" style="width: {size}px; height: {size}px;" '
|
||||
f'src="out/icons_by_id/{self.clear(args[0])}.svg" />'
|
||||
)
|
||||
|
||||
def kbd(self, args: Arguments) -> str:
|
||||
"""Keyboard key."""
|
||||
return f"<kbd>{self.clear(args[0])}</kbd>"
|
||||
|
||||
def page_icon(self, args: Arguments) -> str:
|
||||
"""HTML page icon."""
|
||||
return (
|
||||
f'<link rel="icon" type="image/svg" href="{self.clear(args[0])}"'
|
||||
' sizes="16x16">'
|
||||
)
|
||||
|
||||
def no_wrap(self, args: Arguments) -> str:
|
||||
"""Do not wrap text at white spaces."""
|
||||
return (
|
||||
f'<span style="white-space: nowrap;">{self.parse(args[0])}</span>'
|
||||
)
|
||||
|
||||
def formal(self, args: Arguments) -> str:
|
||||
"""Formal variable."""
|
||||
return f'<span class="formal">{self.parse(args[0])}</span>'
|
||||
|
||||
|
||||
class MapMachineOSMWiki(MapMachineMoire, DefaultWiki):
|
||||
"""
|
||||
OpenStreetMap wiki.
|
||||
|
||||
See https://wiki.openstreetmap.org/wiki/Main_Page
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self.images: dict = {}
|
||||
self.extractor: ShapeExtractor = ShapeExtractor(
|
||||
workspace.ICONS_PATH, workspace.ICONS_CONFIG_PATH
|
||||
)
|
||||
|
||||
def osm(self, args: Arguments) -> str:
|
||||
"""OSM tag key or key–value pair of tag."""
|
||||
spec: str = self.clear(args[0])
|
||||
if "=" in spec:
|
||||
key, tag = spec.split("=")
|
||||
return f"{{{{Key|{key}|{tag}}}}}"
|
||||
else:
|
||||
return f"{{{{Tag|{spec}}}}}"
|
||||
|
||||
def color(self, args: Arguments) -> str:
|
||||
"""Simple color sample."""
|
||||
return f"{{{{Color box|{self.clear(args[0])}}}}}"
|
||||
|
||||
def icon(self, args: Arguments) -> str:
|
||||
"""Image with Röntgen icon."""
|
||||
size: str = self.clear(args[1]) if len(args) > 1 else 16
|
||||
shape_id: str = self.clear(args[0])
|
||||
name: str = self.extractor.get_shape(shape_id).name
|
||||
return f"[[File:Röntgen {name}.svg|{size}px]]"
|
||||
|
||||
|
||||
class MapMachineMarkdown(MapMachineMoire, DefaultMarkdown):
|
||||
"""
|
||||
GitHub flavored markdown.
|
||||
"""
|
||||
|
||||
images = {}
|
||||
|
||||
def color(self, args: Arguments) -> str:
|
||||
"""Simple color sample."""
|
||||
return self.clear(args[0])
|
||||
|
||||
def icon(self, args: Arguments) -> str:
|
||||
"""Image with Röntgen icon."""
|
||||
return f"[{self.clear(args[0])}]"
|
||||
|
||||
def kbd(self, args: Arguments) -> str:
|
||||
"""Keyboard key."""
|
||||
return f"<kbd>{self.clear(args[0])}</kbd>"
|
||||
|
||||
def no_wrap(self, args: Arguments) -> str:
|
||||
"""Do not wrap text at white spaces."""
|
||||
return (
|
||||
f'<span style="white-space: nowrap;">{self.parse(args[0])}</span>'
|
||||
)
|
||||
|
||||
def formal(self, args: Arguments) -> str:
|
||||
"""Formal variable."""
|
||||
return f"<{self.parse(args[0])}>"
|
||||
|
||||
|
||||
def convert(input_path: Path, output_path: Path) -> None:
|
||||
"""Convert Moire file to Markdown."""
|
||||
with input_path.open(encoding="utf-8") as input_file:
|
||||
with output_path.open("w+", encoding="utf-8") as output_file:
|
||||
output_file.write(MapMachineMarkdown().convert(input_file.read()))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
convert(Path("readme.moi"), Path("../../README.md"))
|
||||
convert(Path("contributing.moi"), Path("../../.github/CONTRIBUTING.md"))
|
87
map_machine/doc/taginfo.py
Normal file
87
map_machine/doc/taginfo.py
Normal file
|
@ -0,0 +1,87 @@
|
|||
"""
|
||||
Creating Taginfo project file.
|
||||
|
||||
See https://wiki.openstreetmap.org/wiki/Taginfo/Projects
|
||||
"""
|
||||
import json
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
from map_machine import (
|
||||
__author__,
|
||||
__description__,
|
||||
__doc_url__,
|
||||
__email__,
|
||||
__project__,
|
||||
__url__,
|
||||
)
|
||||
from map_machine.scheme import Scheme
|
||||
from map_machine.workspace import workspace
|
||||
|
||||
|
||||
class TaginfoProjectFile:
|
||||
"""
|
||||
JSON structure with OpenStreetMap tag usage.
|
||||
"""
|
||||
|
||||
def __init__(self, path: Path, scheme: Scheme) -> None:
|
||||
self.path: Path = path
|
||||
|
||||
self.structure = {
|
||||
"data_format": 1,
|
||||
"data_url": __url__ + "/" + str(path),
|
||||
"data_updated": datetime.now().strftime("%Y%m%dT%H%M%SZ"),
|
||||
"project": {
|
||||
"name": __project__,
|
||||
"description": __description__,
|
||||
"project_url": __url__,
|
||||
"doc_url": __doc_url__,
|
||||
"icon_url": "http://enzet.ru/map-machine/image/logo.png",
|
||||
"contact_name": __author__,
|
||||
"contact_email": __email__,
|
||||
},
|
||||
"tags": [],
|
||||
}
|
||||
tags = self.structure["tags"]
|
||||
|
||||
for matcher in scheme.node_matchers:
|
||||
if (
|
||||
not matcher.location_restrictions
|
||||
and matcher.shapes
|
||||
and len(matcher.tags) == 1
|
||||
and not matcher.add_shapes
|
||||
):
|
||||
key: str = list(matcher.tags.keys())[0]
|
||||
value: str = matcher.tags[key]
|
||||
ids: list[str] = [
|
||||
(x if isinstance(x, str) else x["shape"])
|
||||
for x in matcher.shapes
|
||||
]
|
||||
icon_id: str = "___".join(ids)
|
||||
if value == "*":
|
||||
continue
|
||||
tag = {
|
||||
"key": key,
|
||||
"value": value,
|
||||
"object_types": ["node", "area"],
|
||||
"description": "Rendered",
|
||||
"icon_url": "http://enzet.ru/map-machine/"
|
||||
f"roentgen_icons_mapcss/{icon_id}.svg",
|
||||
}
|
||||
tags.append(tag)
|
||||
|
||||
def write(self) -> None:
|
||||
"""Write Taginfo JSON file."""
|
||||
with self.path.open("w+", encoding="utf-8") as output_file:
|
||||
json.dump(self.structure, output_file, indent=4, sort_keys=True)
|
||||
|
||||
|
||||
def write_taginfo_project_file(scheme: Scheme) -> None:
|
||||
"""Write Taginfo JSON file."""
|
||||
out_file: Path = workspace.get_taginfo_file_path()
|
||||
logging.info(f"Write Map Machine project file for Taginfo to {out_file}...")
|
||||
taginfo_project_file: TaginfoProjectFile = TaginfoProjectFile(
|
||||
out_file, scheme
|
||||
)
|
||||
taginfo_project_file.write()
|
Loading…
Add table
Add a link
Reference in a new issue