mirror of
https://github.com/enzet/map-machine.git
synced 2025-08-04 00:59:54 +02:00
Refactor argument parsing.
This commit is contained in:
parent
070ed05c8c
commit
b4682e32d7
6 changed files with 127 additions and 88 deletions
92
roentgen.py
92
roentgen.py
|
@ -8,6 +8,7 @@ import sys
|
|||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
import logging
|
||||
import numpy as np
|
||||
import svgwrite
|
||||
|
||||
|
@ -17,8 +18,13 @@ from roentgen.flinger import Flinger
|
|||
from roentgen.grid import draw_icons, write_mapcss
|
||||
from roentgen.icon import ShapeExtractor
|
||||
from roentgen.mapper import (
|
||||
AUTHOR_MODE, CREATION_TIME_MODE, ICONS_FILE_NAME, Painter, TAGS_FILE_NAME,
|
||||
check_level_number, check_level_overground
|
||||
AUTHOR_MODE,
|
||||
CREATION_TIME_MODE,
|
||||
ICONS_FILE_NAME,
|
||||
Painter,
|
||||
TAGS_FILE_NAME,
|
||||
check_level_number,
|
||||
check_level_overground,
|
||||
)
|
||||
from roentgen.osm_getter import get_osm
|
||||
from roentgen.osm_reader import Map, OSMReader, OverpassReader
|
||||
|
@ -28,16 +34,14 @@ from roentgen.ui import error, parse_options
|
|||
from roentgen.util import MinMax
|
||||
|
||||
|
||||
def main(argv) -> None:
|
||||
def main(options) -> None:
|
||||
"""
|
||||
Röntgen entry point.
|
||||
|
||||
:param argv: command-line arguments
|
||||
"""
|
||||
options: argparse.Namespace = parse_options(argv)
|
||||
|
||||
if not options:
|
||||
sys.exit(1)
|
||||
if options.boundary_box:
|
||||
options.boundary_box = options.boundary_box.replace(" ", "")
|
||||
|
||||
cache_path: Path = Path(options.cache)
|
||||
cache_path.mkdir(parents=True, exist_ok=True)
|
||||
|
@ -65,7 +69,7 @@ def main(argv) -> None:
|
|||
map_ = reader.map_
|
||||
view_box = MinMax(
|
||||
np.array((map_.boundary_box[0].min_, map_.boundary_box[1].min_)),
|
||||
np.array((map_.boundary_box[0].max_, map_.boundary_box[1].max_))
|
||||
np.array((map_.boundary_box[0].max_, map_.boundary_box[1].max_)),
|
||||
)
|
||||
else:
|
||||
is_full: bool = options.mode in [AUTHOR_MODE, CREATION_TIME_MODE]
|
||||
|
@ -82,11 +86,11 @@ def main(argv) -> None:
|
|||
|
||||
if options.boundary_box:
|
||||
boundary_box: List[float] = list(
|
||||
map(float, options.boundary_box.split(','))
|
||||
map(float, options.boundary_box.split(","))
|
||||
)
|
||||
view_box = MinMax(
|
||||
np.array((boundary_box[1], boundary_box[0])),
|
||||
np.array((boundary_box[3], boundary_box[2]))
|
||||
np.array((boundary_box[3], boundary_box[2])),
|
||||
)
|
||||
else:
|
||||
view_box = map_.view_box
|
||||
|
@ -94,6 +98,8 @@ def main(argv) -> None:
|
|||
flinger: Flinger = Flinger(view_box, options.scale)
|
||||
size: np.array = flinger.size
|
||||
|
||||
Path("out").mkdir(parents=True, exist_ok=True)
|
||||
|
||||
svg: svgwrite.Drawing = svgwrite.Drawing(
|
||||
options.output_file_name, size=size
|
||||
)
|
||||
|
@ -102,35 +108,49 @@ def main(argv) -> None:
|
|||
)
|
||||
|
||||
def check_level(x) -> bool:
|
||||
""" Draw objects on all levels. """
|
||||
"""Draw objects on all levels."""
|
||||
return True
|
||||
|
||||
if options.level:
|
||||
if options.level == "overground":
|
||||
check_level = check_level_overground
|
||||
elif options.level == "underground":
|
||||
|
||||
def check_level(x) -> bool:
|
||||
""" Draw underground objects. """
|
||||
"""Draw underground objects."""
|
||||
return not check_level_overground(x)
|
||||
|
||||
else:
|
||||
|
||||
def check_level(x) -> bool:
|
||||
""" Draw objects on the specified level. """
|
||||
"""Draw objects on the specified level."""
|
||||
return not check_level_number(x, float(options.level))
|
||||
|
||||
constructor: Constructor = Constructor(
|
||||
map_, flinger, scheme, icon_extractor, check_level, options.mode,
|
||||
options.seed)
|
||||
map_,
|
||||
flinger,
|
||||
scheme,
|
||||
icon_extractor,
|
||||
check_level,
|
||||
options.mode,
|
||||
options.seed,
|
||||
)
|
||||
constructor.construct()
|
||||
|
||||
painter: Painter = Painter(
|
||||
show_missing_tags=options.show_missing_tags, overlap=options.overlap,
|
||||
mode=options.mode, label_mode=options.label_mode,
|
||||
map_=map_, flinger=flinger, svg=svg, icon_extractor=icon_extractor,
|
||||
scheme=scheme)
|
||||
overlap=options.overlap,
|
||||
mode=options.mode,
|
||||
label_mode=options.label_mode,
|
||||
map_=map_,
|
||||
flinger=flinger,
|
||||
svg=svg,
|
||||
icon_extractor=icon_extractor,
|
||||
scheme=scheme,
|
||||
)
|
||||
|
||||
painter.draw(constructor)
|
||||
|
||||
print("Writing output SVG...")
|
||||
print(f"Writing output SVG to {options.output_file_name}...")
|
||||
with open(options.output_file_name, "w") as output_file:
|
||||
svg.write(output_file)
|
||||
|
||||
|
@ -152,8 +172,13 @@ def draw_element(target: str, tags_description: str):
|
|||
is_for_node: bool = target == "node"
|
||||
labels = scheme.construct_text(tags, "all")
|
||||
point = Point(
|
||||
icon, labels, tags, np.array((32, 32)), None, is_for_node=is_for_node,
|
||||
draw_outline=is_for_node
|
||||
icon,
|
||||
labels,
|
||||
tags,
|
||||
np.array((32, 32)),
|
||||
None,
|
||||
is_for_node=is_for_node,
|
||||
draw_outline=is_for_node,
|
||||
)
|
||||
border: np.array = np.array((16, 16))
|
||||
size: np.array = point.get_size() + border
|
||||
|
@ -171,15 +196,20 @@ def draw_element(target: str, tags_description: str):
|
|||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) == 3 and sys.argv[1] in ["node", "way", "area"]:
|
||||
draw_element(sys.argv[1], sys.argv[2])
|
||||
elif len(sys.argv) == 2 and sys.argv[1] == "icons":
|
||||
|
||||
logging.basicConfig(format='%(levelname)s %(message)s', level=logging.INFO)
|
||||
|
||||
options: argparse.Namespace = parse_options(sys.argv)
|
||||
|
||||
if options.command == "render":
|
||||
main(options)
|
||||
elif options.command == "tile":
|
||||
tile.ui(options)
|
||||
elif options.command == "icons":
|
||||
draw_icons()
|
||||
elif len(sys.argv) == 2 and sys.argv[1] == "mapcss":
|
||||
elif options.command == "mapcss":
|
||||
write_mapcss()
|
||||
elif len(sys.argv) >= 2 and sys.argv[1] == "tile":
|
||||
tile.ui(sys.argv[2:])
|
||||
elif len(sys.argv) >= 2 and sys.argv[1] == "server":
|
||||
elif options.command == "element":
|
||||
draw_element(options)
|
||||
elif options.command == "server":
|
||||
server.ui(sys.argv[2:])
|
||||
else:
|
||||
main(sys.argv)
|
||||
|
|
|
@ -335,8 +335,6 @@ class Constructor:
|
|||
key=lambda x: -self.map_.nodes[x].coordinates[0],
|
||||
)
|
||||
|
||||
missing_tags = Counter()
|
||||
|
||||
for node_id in sorted_node_ids: # type: int
|
||||
processed: Set[str] = set()
|
||||
|
||||
|
@ -383,10 +381,4 @@ class Constructor:
|
|||
) # fmt: skip
|
||||
self.points.append(point)
|
||||
|
||||
missing_tags.update(
|
||||
f"{key}: {tags[key]}"
|
||||
for key in tags
|
||||
if key not in icon_set.processed
|
||||
)
|
||||
|
||||
ui.progress_bar(-1, len(self.map_.nodes), text="Constructing nodes")
|
||||
|
|
|
@ -5,6 +5,7 @@ from dataclasses import dataclass
|
|||
from pathlib import Path
|
||||
from typing import Dict, List, Optional, Set
|
||||
|
||||
import logging
|
||||
import numpy as np
|
||||
from colour import Color
|
||||
from svgwrite import Drawing
|
||||
|
@ -231,9 +232,14 @@ def draw_icons() -> None:
|
|||
Path("icons/icons.svg"), Path("icons/config.json")
|
||||
)
|
||||
collection: IconCollection = IconCollection.from_scheme(scheme, extractor)
|
||||
collection.draw_grid(out_path / "icon_grid.svg")
|
||||
icon_grid_path: Path = out_path / "icon_grid.svg"
|
||||
collection.draw_grid(icon_grid_path)
|
||||
logging.info(f"Icon grid is written to {icon_grid_path}.")
|
||||
collection.draw_icons(icons_by_id_path)
|
||||
collection.draw_icons(icons_by_name_path, by_name=True)
|
||||
logging.info(
|
||||
f"Icons are written to {icons_by_name_path} and {icons_by_id_path}."
|
||||
)
|
||||
|
||||
|
||||
def write_mapcss() -> None:
|
||||
|
@ -241,7 +247,11 @@ def write_mapcss() -> None:
|
|||
Write MapCSS 0.2 scheme.
|
||||
"""
|
||||
out_path: Path = Path("out")
|
||||
icons_with_outline_path: Path = out_path / "roentgen_icons" / "icons"
|
||||
directory: Path = (out_path / "roentgen_icons_mapcss")
|
||||
directory.mkdir(exist_ok=True)
|
||||
icons_with_outline_path: Path = directory / "icons"
|
||||
|
||||
icons_with_outline_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
scheme: Scheme = Scheme(Path("scheme/default.yml"))
|
||||
extractor: ShapeExtractor = ShapeExtractor(
|
||||
|
@ -251,13 +261,12 @@ def write_mapcss() -> None:
|
|||
collection.draw_icons(
|
||||
icons_with_outline_path, color=Color("black"), outline=True
|
||||
)
|
||||
(out_path / "roentgen_icons").mkdir(exist_ok=True)
|
||||
with Path("data/roentgen_icons_part.mapcss").open() as input_file:
|
||||
with (out_path / "roentgen_icons" / "roentgen_icons.mapcss").open(
|
||||
"w+"
|
||||
) as output_file:
|
||||
with (directory / "roentgen_icons.mapcss").open("w+") as output_file:
|
||||
for line in input_file.readlines():
|
||||
if line == "%CONTENT%\n":
|
||||
output_file.write(collection.get_mapcss_selectors())
|
||||
else:
|
||||
output_file.write(line)
|
||||
|
||||
logging.info(f"MapCSS 0.2 scheme is written to {directory}.")
|
||||
|
|
|
@ -26,7 +26,6 @@ __email__ = "me@enzet.ru"
|
|||
|
||||
ICONS_FILE_NAME: str = "icons/icons.svg"
|
||||
TAGS_FILE_NAME: str = "scheme/default.yml"
|
||||
MISSING_TAGS_FILE_NAME: str = "missing_tags.yml"
|
||||
|
||||
AUTHOR_MODE = "user-coloring"
|
||||
CREATION_TIME_MODE = "time"
|
||||
|
@ -44,12 +43,10 @@ class Painter:
|
|||
svg: svgwrite.Drawing,
|
||||
icon_extractor: ShapeExtractor,
|
||||
scheme: Scheme,
|
||||
show_missing_tags: bool = False,
|
||||
overlap: int = 12,
|
||||
mode: str = "normal",
|
||||
label_mode: str = "main",
|
||||
):
|
||||
self.show_missing_tags: bool = show_missing_tags
|
||||
self.overlap: int = overlap
|
||||
self.mode: str = mode
|
||||
self.label_mode: str = label_mode
|
||||
|
|
|
@ -86,7 +86,7 @@ class Tile:
|
|||
)
|
||||
return np.array(extended_1), np.array(extended_2)
|
||||
|
||||
def load_map(self) -> Optional[Map]:
|
||||
def load_map(self, cache_path: Path) -> Optional[Map]:
|
||||
"""
|
||||
Construct map data from extended boundary box.
|
||||
"""
|
||||
|
@ -103,7 +103,7 @@ class Tile:
|
|||
error("cannot download OSM data")
|
||||
return None
|
||||
|
||||
return OSMReader().parse_osm_file("map" / Path(boundary_box + ".osm"))
|
||||
return OSMReader().parse_osm_file(cache_path / (boundary_box + ".osm"))
|
||||
|
||||
def get_map_name(self, directory_name: Path) -> Path:
|
||||
"""
|
||||
|
@ -119,13 +119,13 @@ class Tile:
|
|||
f"https://tile.openstreetmap.org/{self.scale}/{self.x}/{self.y}.png"
|
||||
)
|
||||
|
||||
def draw(self, directory_name: Path):
|
||||
def draw(self, directory_name: Path, cache_path: Path):
|
||||
"""
|
||||
Draw tile to SVG file.
|
||||
|
||||
:param directory_name: output directory to storing tiles
|
||||
"""
|
||||
map_ = self.load_map()
|
||||
map_ = self.load_map(cache_path)
|
||||
|
||||
lat1, lon1 = self.get_coordinates()
|
||||
lat2, lon2 = Tile(self.x + 1, self.y + 1, self.scale).get_coordinates()
|
||||
|
@ -164,18 +164,12 @@ class Tile:
|
|||
svg.write(output_file)
|
||||
|
||||
|
||||
def ui(args) -> None:
|
||||
def ui(options) -> None:
|
||||
"""
|
||||
Simple user interface for tile generation.
|
||||
"""
|
||||
parser: argparse.ArgumentParser = argparse.ArgumentParser()
|
||||
parser.add_argument("-c")
|
||||
parser.add_argument("-s")
|
||||
parser.add_argument("-t")
|
||||
options = parser.parse_args(args)
|
||||
|
||||
directory: Path = Path("tiles")
|
||||
directory.mkdir(exist_ok=True)
|
||||
directory: Path = Path("out/tiles")
|
||||
directory.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
tile: Tile
|
||||
if options.c and options.s:
|
||||
|
@ -187,5 +181,5 @@ def ui(args) -> None:
|
|||
else:
|
||||
sys.exit(1)
|
||||
|
||||
tile.draw(directory)
|
||||
tile.draw(directory, Path(options.cache))
|
||||
print(tile.get_carto_address())
|
||||
|
|
|
@ -20,66 +20,83 @@ def parse_options(args) -> argparse.Namespace:
|
|||
"""
|
||||
Parse Röntgen command-line options.
|
||||
"""
|
||||
parser = argparse.ArgumentParser()
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Röntgen. OpenStreetMap renderer with custom icon set"
|
||||
)
|
||||
subparser = parser.add_subparsers(dest="command")
|
||||
|
||||
parser.add_argument(
|
||||
render = subparser.add_parser("render")
|
||||
icons = subparser.add_parser("icons")
|
||||
mapcss = subparser.add_parser("mapcss")
|
||||
tile = subparser.add_parser("tile")
|
||||
|
||||
render.add_argument(
|
||||
"-i",
|
||||
"--input",
|
||||
dest="input_file_name",
|
||||
metavar="<path>",
|
||||
nargs="*",
|
||||
help="input XML file name (if not specified, file will be downloaded "
|
||||
"using OpenStreetMap API)",
|
||||
help="input XML file name or names (if not specified, file will be "
|
||||
"downloaded using OpenStreetMap API)",
|
||||
)
|
||||
parser.add_argument(
|
||||
render.add_argument(
|
||||
"-o",
|
||||
"--output",
|
||||
dest="output_file_name",
|
||||
metavar="<path>",
|
||||
default="map.svg",
|
||||
help="output SVG file name (map.svg by default)",
|
||||
default="out/map.svg",
|
||||
help="output SVG file name (out/map.svg by default)",
|
||||
)
|
||||
parser.add_argument(
|
||||
render.add_argument(
|
||||
"-b",
|
||||
"--boundary-box",
|
||||
dest="boundary_box",
|
||||
metavar="<lon1>,<lat1>,<lon2>,<lat2>",
|
||||
help='geo boundary box, use space before "-" for negative values',
|
||||
help='geo boundary box, use space before "-" if the first value is '
|
||||
"negative",
|
||||
)
|
||||
parser.add_argument(
|
||||
render.add_argument(
|
||||
"-s",
|
||||
"--scale",
|
||||
metavar="<float>",
|
||||
help="OSM zoom level (may not be integer, default is 18)",
|
||||
default=18,
|
||||
dest="scale",
|
||||
type=float,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--cache", help="path for temporary OSM files", default="cache"
|
||||
render.add_argument(
|
||||
"--cache",
|
||||
help="path for temporary OSM files",
|
||||
default="cache",
|
||||
metavar="<path>",
|
||||
)
|
||||
parser.add_argument(
|
||||
render.add_argument(
|
||||
"--labels",
|
||||
help="label drawing mode: `no`, `main`, or `all`",
|
||||
dest="label_mode",
|
||||
default="main",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--show-missing-tags", dest="show_missing_tags", action="store_true"
|
||||
render.add_argument(
|
||||
"--overlap",
|
||||
dest="overlap",
|
||||
default=12,
|
||||
type=int,
|
||||
help="how many pixels should be left around icons and text",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-show-missing-tags", dest="show_missing_tags", action="store_false"
|
||||
render.add_argument("--mode", default="normal")
|
||||
render.add_argument("--seed", default="")
|
||||
render.add_argument("--level", default=None)
|
||||
|
||||
tile.add_argument("-c")
|
||||
tile.add_argument("-s")
|
||||
tile.add_argument("-t")
|
||||
tile.add_argument(
|
||||
"--cache",
|
||||
help="path for temporary OSM files",
|
||||
default="cache",
|
||||
metavar="<path>",
|
||||
)
|
||||
parser.add_argument("--overlap", dest="overlap", default=12, type=int)
|
||||
parser.add_argument("--mode", default="normal")
|
||||
parser.add_argument("--seed", default="")
|
||||
parser.add_argument("--level", default=None)
|
||||
|
||||
arguments: argparse.Namespace = parser.parse_args(args[1:])
|
||||
|
||||
if arguments.boundary_box:
|
||||
arguments.boundary_box = arguments.boundary_box.replace(" ", "")
|
||||
|
||||
return arguments
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue