mirror of
https://github.com/enzet/map-machine.git
synced 2025-04-28 17:57:11 +02:00
395 lines
11 KiB
Python
395 lines
11 KiB
Python
"""Command-line user interface."""
|
||
import argparse
|
||
from typing import Dict, List
|
||
|
||
from map_machine import __version__
|
||
from map_machine.map_configuration import BuildingMode, DrawingMode, LabelMode
|
||
from map_machine.osm.osm_reader import STAGES_OF_DECAY
|
||
|
||
__author__ = "Sergey Vartanov"
|
||
__email__ = "me@enzet.ru"
|
||
|
||
COMMAND_LINES: Dict[str, List[str]] = {
|
||
"render": ["render", "-b", "10.000,20.000,10.001,20.001"],
|
||
"render_with_tooltips": [
|
||
"render",
|
||
"-b",
|
||
"10.000,20.000,10.001,20.001",
|
||
"--tooltips",
|
||
],
|
||
"icons": ["icons"],
|
||
"mapcss": ["mapcss"],
|
||
"draw": ["draw", "node", "amenity=bench,material=wood"],
|
||
"tile": ["tile", "--coordinates", "50.000,40.000"],
|
||
}
|
||
COMMANDS: List[str] = [
|
||
"render",
|
||
"server",
|
||
"tile",
|
||
"element",
|
||
"mapcss",
|
||
"icons",
|
||
"taginfo",
|
||
]
|
||
|
||
BOUNDARY_BOX_WARNING: str = (
|
||
"if the first value is negative, use `=` sign or enclose the value with "
|
||
"quotes and use space before `-`, e.g. `-b=-84.752,39.504,-84.749,39.508` "
|
||
'or `-b " -84.752,39.504,-84.749,39.508"`'
|
||
)
|
||
COORDINATES_WARNING: str = (
|
||
"if the first value is negative, use `=` sign or enclose the value with "
|
||
"quotes and use space before `-`, e.g. `-c=-84.752,39.504` or `-c "
|
||
'" -84.752,39.504"`'
|
||
)
|
||
|
||
|
||
def parse_arguments(args: List[str]) -> argparse.Namespace:
|
||
"""Parse Map Machine command-line arguments."""
|
||
parser: argparse.ArgumentParser = argparse.ArgumentParser(
|
||
description="Map Machine. OpenStreetMap renderer with custom icon set"
|
||
)
|
||
parser.add_argument(
|
||
"-v",
|
||
"--version",
|
||
action="version",
|
||
version="Map Machine " + __version__,
|
||
)
|
||
subparser = parser.add_subparsers(dest="command")
|
||
|
||
render_parser = subparser.add_parser(
|
||
"render",
|
||
description="Render SVG map. Use --boundary-box to specify geo "
|
||
"boundaries, --input to specify OSM XML or JSON input file, or "
|
||
"--coordinates and --size to specify central point and resulting image "
|
||
"size.",
|
||
help="draw SVG map",
|
||
)
|
||
add_render_arguments(render_parser)
|
||
add_map_arguments(render_parser)
|
||
|
||
tile_parser = subparser.add_parser(
|
||
"tile",
|
||
description="Generate SVG and PNG 256 × 256 px tiles for slippy maps. "
|
||
"You can use server command to run server in order to display "
|
||
"generated tiles as a map (e.g. with Leaflet).",
|
||
help="generate SVG and PNG tiles for slippy maps",
|
||
)
|
||
add_tile_arguments(tile_parser)
|
||
add_map_arguments(tile_parser)
|
||
|
||
add_server_arguments(
|
||
subparser.add_parser(
|
||
"server",
|
||
description="Run in order to display generated tiles as a map "
|
||
"(e.g. with Leaflet).",
|
||
help="run tile server",
|
||
)
|
||
)
|
||
add_draw_arguments(
|
||
subparser.add_parser(
|
||
"draw",
|
||
description="Draw map element separately.",
|
||
help="draw OSM element: node, way, relation",
|
||
)
|
||
)
|
||
add_mapcss_arguments(
|
||
subparser.add_parser(
|
||
"mapcss",
|
||
description="Write directory with MapCSS file and generated "
|
||
"Röntgen icons.",
|
||
help="write MapCSS file",
|
||
)
|
||
)
|
||
|
||
subparser.add_parser(
|
||
"icons",
|
||
description="Generate Röntgen icons as a grid and as separate SVG "
|
||
"icons",
|
||
help="draw Röntgen icons",
|
||
)
|
||
subparser.add_parser(
|
||
"taginfo",
|
||
description="Generate JSON file for Taginfo project.",
|
||
help="write Taginfo JSON file",
|
||
)
|
||
|
||
arguments: argparse.Namespace = parser.parse_args(args[1:])
|
||
|
||
return arguments
|
||
|
||
|
||
def add_map_arguments(parser: argparse.ArgumentParser) -> None:
|
||
"""Add map-specific arguments."""
|
||
parser.add_argument(
|
||
"--scheme",
|
||
metavar="<id> or <path>",
|
||
default="default",
|
||
help="scheme identifier (look for `<id>.yml` file) or path to a YAML "
|
||
"scheme file",
|
||
)
|
||
parser.add_argument(
|
||
"--buildings",
|
||
metavar="<mode>",
|
||
default="flat",
|
||
choices=(mode.value for mode in BuildingMode),
|
||
help="building drawing mode: "
|
||
+ ", ".join(mode.value for mode in BuildingMode),
|
||
)
|
||
parser.add_argument(
|
||
"--mode",
|
||
default="normal",
|
||
metavar="<string>",
|
||
choices=(mode.value for mode in DrawingMode),
|
||
help="map drawing mode: "
|
||
+ ", ".join(mode.value for mode in DrawingMode),
|
||
)
|
||
parser.add_argument(
|
||
"--overlap",
|
||
dest="overlap",
|
||
default=12,
|
||
type=int,
|
||
help="how many pixels should be left around icons and text",
|
||
metavar="<integer>",
|
||
)
|
||
parser.add_argument(
|
||
"--labels",
|
||
dest="label_mode",
|
||
default="main",
|
||
metavar="<string>",
|
||
choices=(mode.value for mode in LabelMode),
|
||
help="label drawing mode: "
|
||
+ ", ".join(mode.value for mode in LabelMode),
|
||
)
|
||
parser.add_argument(
|
||
"--level",
|
||
default="overground",
|
||
help="display only this floor level",
|
||
)
|
||
parser.add_argument(
|
||
"--seed",
|
||
default="",
|
||
help="seed for random",
|
||
metavar="<string>",
|
||
)
|
||
parser.add_argument(
|
||
"--tooltips",
|
||
help="add tooltips with tags for icons in SVG files",
|
||
action="store_true",
|
||
default=False,
|
||
)
|
||
parser.add_argument(
|
||
"--no-tooltips",
|
||
dest="tooltips",
|
||
help="don't add tooltips with tags for icons in SVG files",
|
||
action="store_false",
|
||
)
|
||
parser.add_argument(
|
||
"--country",
|
||
help="two-letter code (ISO 3166-1 alpha-2) of country, that should be "
|
||
"used for location restrictions",
|
||
default="world",
|
||
)
|
||
parser.add_argument(
|
||
"--ignore-level-matching",
|
||
help="draw all map features ignoring the current level",
|
||
action="store_true",
|
||
default=False,
|
||
)
|
||
parser.add_argument(
|
||
"--no-ignore-level-matching",
|
||
dest="ignore_level_matching",
|
||
help="draw all map features taking into account the current level",
|
||
action="store_false",
|
||
)
|
||
parser.add_argument(
|
||
"--roofs",
|
||
help="draw building roofs",
|
||
action="store_true",
|
||
default=True,
|
||
)
|
||
parser.add_argument(
|
||
"--building-colors",
|
||
help="paint walls (if isometric mode is enabled) and roofs with "
|
||
"specified colors",
|
||
action="store_true",
|
||
default=False,
|
||
)
|
||
parser.add_argument(
|
||
"--no-building-colors",
|
||
help="don't paint walls (if isometric mode is enabled) and roofs with "
|
||
"specified colors",
|
||
action="store_false",
|
||
)
|
||
parser.add_argument(
|
||
"--show-overlapped",
|
||
help="show hidden nodes with a dot",
|
||
action="store_true",
|
||
default=False,
|
||
)
|
||
parser.add_argument(
|
||
"--no-show-overlapped",
|
||
help="don't show hidden nodes with a dot",
|
||
action="store_false",
|
||
)
|
||
|
||
|
||
def add_tile_arguments(parser: argparse.ArgumentParser) -> None:
|
||
"""Add arguments for tile command."""
|
||
parser.add_argument(
|
||
"-c",
|
||
"--coordinates",
|
||
metavar="<latitude>,<longitude>",
|
||
help="coordinates of any location inside the tile; "
|
||
+ COORDINATES_WARNING,
|
||
)
|
||
parser.add_argument(
|
||
"-t",
|
||
"--tile",
|
||
metavar="<zoom level>/<x>/<y>",
|
||
help="tile specification",
|
||
)
|
||
parser.add_argument(
|
||
"--cache",
|
||
help="path for temporary OSM files",
|
||
default="cache",
|
||
metavar="<path>",
|
||
)
|
||
parser.add_argument(
|
||
"-b",
|
||
"--boundary-box",
|
||
help="construct the minimum amount of tiles that cover the requested "
|
||
"boundary box; " + BOUNDARY_BOX_WARNING,
|
||
metavar="<lon1>,<lat1>,<lon2>,<lat2>",
|
||
)
|
||
parser.add_argument(
|
||
"-z",
|
||
"--zoom",
|
||
type=str,
|
||
metavar="<range>",
|
||
help="OSM zoom levels; can be list of numbers or ranges, e.g. `16-18`, "
|
||
"`16,17,18`, or `16,18-20`",
|
||
default="18",
|
||
)
|
||
parser.add_argument(
|
||
"-i",
|
||
"--input",
|
||
dest="input_file_name",
|
||
metavar="<path>",
|
||
help="input OSM XML file name (if not specified, the file will be "
|
||
"downloaded using OpenStreetMap API)",
|
||
)
|
||
|
||
|
||
def add_server_arguments(parser: argparse.ArgumentParser) -> None:
|
||
"""Add arguments for server command."""
|
||
parser.add_argument(
|
||
"--cache",
|
||
help="path for temporary OSM files",
|
||
default="cache",
|
||
metavar="<path>",
|
||
)
|
||
parser.add_argument(
|
||
"--port",
|
||
help="port number",
|
||
default=8080,
|
||
type=int,
|
||
metavar="<integer>",
|
||
)
|
||
|
||
|
||
def add_draw_arguments(parser: argparse.ArgumentParser) -> None:
|
||
"""Add arguments for element command."""
|
||
parser.add_argument("type")
|
||
parser.add_argument("tags")
|
||
parser.add_argument("-o", "--output-file", default="out/element.svg")
|
||
|
||
|
||
def add_render_arguments(parser: argparse.ArgumentParser) -> None:
|
||
"""Add arguments for render command."""
|
||
parser.add_argument(
|
||
"-i",
|
||
"--input",
|
||
dest="input_file_names",
|
||
metavar="<path>",
|
||
nargs="*",
|
||
help="input XML file name or names (if not specified, file will be "
|
||
"downloaded using OpenStreetMap API)",
|
||
)
|
||
parser.add_argument(
|
||
"-o",
|
||
"--output",
|
||
dest="output_file_name",
|
||
metavar="<path>",
|
||
default="out/map.svg",
|
||
help="output SVG file name",
|
||
)
|
||
parser.add_argument(
|
||
"-b",
|
||
"--boundary-box",
|
||
metavar="<lon1>,<lat1>,<lon2>,<lat2>",
|
||
help="geo boundary box; " + BOUNDARY_BOX_WARNING,
|
||
)
|
||
parser.add_argument(
|
||
"--cache",
|
||
help="path for temporary OSM files",
|
||
default="cache",
|
||
metavar="<path>",
|
||
)
|
||
parser.add_argument(
|
||
"-z",
|
||
"--zoom",
|
||
type=float,
|
||
metavar="<float>",
|
||
help="OSM zoom level",
|
||
default=18.0,
|
||
)
|
||
parser.add_argument(
|
||
"-c",
|
||
"--coordinates",
|
||
metavar="<latitude>,<longitude>",
|
||
help="coordinates of any location inside the tile; "
|
||
+ COORDINATES_WARNING,
|
||
)
|
||
parser.add_argument(
|
||
"-s",
|
||
"--size",
|
||
metavar="<width>,<height>",
|
||
help="resulted image size",
|
||
)
|
||
|
||
|
||
def add_mapcss_arguments(parser: argparse.ArgumentParser) -> None:
|
||
"""Add arguments for mapcss command."""
|
||
parser.add_argument(
|
||
"--icons",
|
||
action="store_true",
|
||
default=True,
|
||
help="add icons for nodes and areas",
|
||
)
|
||
parser.add_argument(
|
||
"--no-icons",
|
||
dest="icons",
|
||
action="store_false",
|
||
help="don't add icons for nodes and areas",
|
||
)
|
||
parser.add_argument(
|
||
"--ways",
|
||
action="store_true",
|
||
default=False,
|
||
help="add style for ways and relations",
|
||
)
|
||
parser.add_argument(
|
||
"--no-ways",
|
||
dest="ways",
|
||
action="store_false",
|
||
help="don't add style for ways and relations",
|
||
)
|
||
parser.add_argument(
|
||
"--lifecycle",
|
||
action="store_true",
|
||
default=True,
|
||
help="add icons for lifecycle tags; be careful: this will increase the "
|
||
f"number of node and area selectors by {len(STAGES_OF_DECAY) + 1} "
|
||
f"times",
|
||
)
|