mirror of
https://github.com/enzet/map-machine.git
synced 2025-08-02 08:09:57 +02:00
Add SVG table drawing.
This commit is contained in:
parent
b10b2d255d
commit
f893b1697e
1 changed files with 284 additions and 2 deletions
|
@ -2,9 +2,87 @@
|
|||
Special icon collections for documentation.
|
||||
"""
|
||||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
import numpy as np
|
||||
import svgwrite
|
||||
from svgwrite import Drawing
|
||||
|
||||
from map_machine.map_configuration import MapConfiguration
|
||||
from map_machine.osm.osm_reader import Tags
|
||||
from map_machine.pictogram.icon import ShapeExtractor, Icon
|
||||
from map_machine.scheme import Scheme
|
||||
from map_machine.workspace import Workspace
|
||||
|
||||
WORKSPACE: Workspace = Workspace(Path("temp"))
|
||||
|
||||
SCHEME: Scheme = Scheme(WORKSPACE.DEFAULT_SCHEME_PATH)
|
||||
EXTRACTOR: ShapeExtractor = ShapeExtractor(
|
||||
WORKSPACE.ICONS_PATH, WORKSPACE.ICONS_CONFIG_PATH
|
||||
)
|
||||
|
||||
|
||||
class SVGTable:
|
||||
"""SVG table with icon combinations."""
|
||||
|
||||
def __init__(self, svg):
|
||||
self.svg = svg
|
||||
|
||||
self.border: np.ndarray = np.array((16.0, 16.0))
|
||||
self.step: float = 48.0
|
||||
self.icon_size: float = 32.0
|
||||
self.font_size: float = 10.0
|
||||
self.offset: float = 30.0
|
||||
self.half_step: np.ndarray = np.array(
|
||||
(self.step / 2.0, self.step / 2.0)
|
||||
)
|
||||
|
||||
fonts: list[str] = [
|
||||
"JetBrains Mono",
|
||||
"Fira Code",
|
||||
"Fira Mono",
|
||||
"ui-monospace",
|
||||
"SFMono-regular",
|
||||
"SF Mono",
|
||||
"Menlo",
|
||||
"Consolas",
|
||||
"Liberation Mono",
|
||||
"monospace",
|
||||
]
|
||||
self.font: str = ",".join(fonts)
|
||||
self.font_width: float = self.font_size * 0.7
|
||||
|
||||
def draw_text(self, text, point):
|
||||
text = self.svg.text(
|
||||
text,
|
||||
point,
|
||||
font_family=self.font,
|
||||
font_size=self.font_size,
|
||||
text_anchor="end",
|
||||
)
|
||||
self.svg.add(text)
|
||||
|
||||
def draw_bold_text(self, text, point):
|
||||
text = self.svg.text(
|
||||
text,
|
||||
point,
|
||||
font_family=self.font,
|
||||
font_size=self.font_size,
|
||||
font_weight="bold",
|
||||
text_anchor="end",
|
||||
)
|
||||
self.svg.add(text)
|
||||
|
||||
def draw_cross(self, point):
|
||||
for vector in np.array((1, 1)), np.array((1, -1)):
|
||||
line = self.svg.line(
|
||||
point - 15 * vector,
|
||||
point + 15 * vector,
|
||||
stroke_width=0.5,
|
||||
stroke="black",
|
||||
)
|
||||
self.svg.add(line)
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -12,12 +90,200 @@ class Collection:
|
|||
"""Icon collection."""
|
||||
|
||||
page_name: str
|
||||
# Core tags
|
||||
tags: Tags
|
||||
row_key: str = None
|
||||
# Tag key to be used in rows
|
||||
row_key: Optional[str] = None
|
||||
# List of tag values to be used in rows
|
||||
row_values: list[str] = field(default_factory=list)
|
||||
# Tag key to be used in columns
|
||||
column_key: Optional[str] = None
|
||||
# List of tag values to be used in columns
|
||||
column_values: list[str] = field(default_factory=list)
|
||||
|
||||
def generate_table(self) -> tuple[str, list[Icon]]:
|
||||
"""
|
||||
Generate Röntgen icon table for the OpenStreetMap wiki page.
|
||||
|
||||
"""
|
||||
icons: list[Icon] = []
|
||||
text: str = '{| class="wikitable"\n'
|
||||
|
||||
if self.column_key is not None:
|
||||
text += f"! {{{{Key|{self.column_key}}}}}"
|
||||
else:
|
||||
text += "! Tag || Icon"
|
||||
|
||||
if not self.column_values:
|
||||
self.column_values = [""]
|
||||
else:
|
||||
for column_value in self.column_values:
|
||||
text += " ||"
|
||||
if column_value:
|
||||
text += (
|
||||
" {{vert header|"
|
||||
f"{{{{TagValue|{self.column_key}|{column_value}}}}}"
|
||||
"}}"
|
||||
)
|
||||
text += "\n"
|
||||
|
||||
processed: set[str] = set()
|
||||
|
||||
for row_value in self.row_values:
|
||||
text += "|-\n"
|
||||
if row_value:
|
||||
text += f"| {{{{Tag|{self.row_key}|{row_value}}}}}\n"
|
||||
else:
|
||||
text += "|\n"
|
||||
for column_value in self.column_values:
|
||||
current_tags: Tags = dict(self.tags) | {self.row_key: row_value}
|
||||
if column_value:
|
||||
current_tags |= {self.column_key: column_value}
|
||||
icon, _ = SCHEME.get_icon(
|
||||
EXTRACTOR, current_tags, processed, MapConfiguration()
|
||||
)
|
||||
if not icon:
|
||||
print("Icon was not constructed.")
|
||||
text += (
|
||||
"| "
|
||||
f"[[Image:Röntgen {icon.main_icon.get_name()}.svg|32px]]\n"
|
||||
)
|
||||
icons.append(icon.main_icon)
|
||||
|
||||
text += "|}\n"
|
||||
|
||||
return text, icons
|
||||
|
||||
def draw_table(self, svg: Drawing):
|
||||
"""Draw SVG table."""
|
||||
table = SVGTable(svg)
|
||||
|
||||
border: np.ndarray = np.array((16.0, 16.0))
|
||||
step: float = 48.0
|
||||
icon_size: float = 32.0
|
||||
font_size: float = 10.0
|
||||
offset: float = 30.0
|
||||
half_step: np.ndarray = np.array((step / 2.0, step / 2.0))
|
||||
|
||||
fonts: list[str] = [
|
||||
"JetBrains Mono",
|
||||
"Fira Code",
|
||||
"Fira Mono",
|
||||
"monospace",
|
||||
]
|
||||
font: str = ",".join(fonts)
|
||||
font_width: float = font_size * 0.62
|
||||
|
||||
size: list[float] = [
|
||||
max(
|
||||
max(map(len, self.row_values)) * font_width,
|
||||
(len(self.row_key) + 2) * font_width
|
||||
+ (offset if self.column_values else 0),
|
||||
)
|
||||
if self.row_values
|
||||
else 25.0,
|
||||
max(map(len, self.column_values)) * font_width
|
||||
if self.column_values
|
||||
else 25.0,
|
||||
]
|
||||
|
||||
start_point: np.ndarray = 2 * border + np.array(size) + half_step
|
||||
point = np.array(start_point) - np.array((step / 2.0 + border[0], 0.0))
|
||||
shift = (
|
||||
-offset if self.column_values else 0,
|
||||
2.0 - step / 2.0 - border[1],
|
||||
)
|
||||
|
||||
if self.row_key:
|
||||
table.draw_bold_text(f"{self.row_key}=*", point + np.array(shift))
|
||||
|
||||
if self.column_key:
|
||||
table.draw_bold_text(
|
||||
f"{self.column_key}=*",
|
||||
point + np.array((0.0, 2.0 - step / 2.0 - border[0] - offset)),
|
||||
)
|
||||
|
||||
for row_value in self.row_values:
|
||||
if row_value:
|
||||
table.draw_text(row_value, point + np.array((0.0, 2.0)))
|
||||
point += np.array((0, step))
|
||||
|
||||
if self.column_values:
|
||||
line = svg.line(
|
||||
start_point - half_step - border,
|
||||
start_point - half_step - border - np.array((15, 15)),
|
||||
stroke_width=0.5,
|
||||
stroke="black",
|
||||
)
|
||||
svg.add(line)
|
||||
|
||||
if not self.column_values:
|
||||
self.column_values = [""]
|
||||
|
||||
rectangle = svg.rect(
|
||||
start_point - half_step,
|
||||
np.array((len(self.column_values), len(self.row_values))) * step,
|
||||
fill="#fea",
|
||||
)
|
||||
svg.add(rectangle)
|
||||
|
||||
point = np.array(start_point)
|
||||
|
||||
for column_value in self.column_values:
|
||||
text_point = point + np.array((2, -step / 2.0 - border[1]))
|
||||
text = svg.text(
|
||||
f"{column_value}",
|
||||
text_point,
|
||||
font_family=font,
|
||||
font_size=font_size,
|
||||
transform=f"rotate(270,{text_point[0]},{text_point[1]})",
|
||||
)
|
||||
svg.add(text)
|
||||
point += np.array((step, 0.0))
|
||||
|
||||
point = np.array(start_point)
|
||||
|
||||
last = None
|
||||
|
||||
for row_value in self.row_values:
|
||||
for column_value in self.column_values:
|
||||
current_tags: Tags = dict(self.tags) | {self.row_key: row_value}
|
||||
if column_value:
|
||||
current_tags |= {self.column_key: column_value}
|
||||
processed: set[str] = set()
|
||||
icon, _ = SCHEME.get_icon(
|
||||
EXTRACTOR, current_tags, processed, MapConfiguration()
|
||||
)
|
||||
processed = icon.processed
|
||||
if not icon:
|
||||
print("Icon was not constructed.")
|
||||
|
||||
if (
|
||||
icon.main_icon
|
||||
and not icon.main_icon.is_default()
|
||||
and (
|
||||
not self.column_key
|
||||
or not column_value
|
||||
or (self.column_key in processed)
|
||||
)
|
||||
and (
|
||||
not self.row_key
|
||||
or not row_value
|
||||
or (self.row_key in processed)
|
||||
)
|
||||
):
|
||||
icon.main_icon.draw(svg, point, scale=icon_size / 16.0)
|
||||
else:
|
||||
table.draw_cross(point)
|
||||
last = np.array(point)
|
||||
point += np.array((step, 0.0))
|
||||
point = np.array((start_point[0], point[1] + step))
|
||||
|
||||
if last is None:
|
||||
return point
|
||||
else:
|
||||
return last + np.array((step / 2, step / 2)) + border
|
||||
|
||||
|
||||
tower_type_values: list[str] = [
|
||||
"communication",
|
||||
|
@ -41,7 +307,7 @@ collections = [
|
|||
"volcano:type",
|
||||
["stratovolcano", "shield", "scoria"],
|
||||
"volcano:status",
|
||||
["active", "dormant", "extinct"],
|
||||
["", "active", "dormant", "extinct"],
|
||||
),
|
||||
Collection(
|
||||
"Tag:tower:construction=guyed_tube",
|
||||
|
@ -122,3 +388,19 @@ collections = [
|
|||
],
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
def main() -> None:
|
||||
with Path("result.html").open("w+") as html_file:
|
||||
for collection in collections:
|
||||
path: Path = Path("doc") / f"{collection.page_name}.svg"
|
||||
svg: svgwrite = svgwrite.Drawing(path.name)
|
||||
width, height = collection.draw_table(svg)
|
||||
svg.update({"width": width, "height": height})
|
||||
with path.open("w+") as output_file:
|
||||
svg.write(output_file)
|
||||
html_file.write(f'<img src="doc/{path.name}" />\n')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue