mirror of
https://github.com/enzet/map-machine.git
synced 2025-05-02 11:46:41 +02:00
Add --coordinates and --size arguments.
Construct boundary box from center coordinates and size.
This commit is contained in:
parent
cb440e8a8b
commit
b715e12924
8 changed files with 156 additions and 114 deletions
|
@ -76,6 +76,45 @@ class BoundaryBox:
|
||||||
|
|
||||||
return cls(left, bottom, right, top)
|
return cls(left, bottom, right, top)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_coordinates(
|
||||||
|
cls,
|
||||||
|
coordinates: np.ndarray,
|
||||||
|
zoom_level: float,
|
||||||
|
width: float,
|
||||||
|
height: float,
|
||||||
|
) -> "BoundaryBox":
|
||||||
|
"""
|
||||||
|
Compute boundary box from central coordinates, zoom level and resulting
|
||||||
|
image size.
|
||||||
|
|
||||||
|
:param coordinates: boundary box central coordinates
|
||||||
|
:param zoom_level: resulting image zoom level
|
||||||
|
:param width: resulting image width
|
||||||
|
:param height: resulting image height
|
||||||
|
"""
|
||||||
|
lat_rad: np.ndarray = np.radians(coordinates[0])
|
||||||
|
n: float = 2.0 ** (zoom_level + 8.0)
|
||||||
|
|
||||||
|
x: int = int((coordinates[1] + 180.0) / 360.0 * n)
|
||||||
|
left: float = (x - width / 2) / n * 360.0 - 180.0
|
||||||
|
right: float = (x + width / 2) / n * 360.0 - 180.0
|
||||||
|
|
||||||
|
y: int = (1.0 - np.arcsinh(np.tan(lat_rad)) / np.pi) / 2.0 * n
|
||||||
|
bottom_radians = np.arctan(
|
||||||
|
np.sinh((1.0 - (y + height / 2) * 2.0 / n) * np.pi)
|
||||||
|
)
|
||||||
|
top_radians = np.arctan(
|
||||||
|
np.sinh((1.0 - (y - height / 2) * 2.0 / n) * np.pi)
|
||||||
|
)
|
||||||
|
|
||||||
|
return cls(
|
||||||
|
left,
|
||||||
|
float(np.degrees(bottom_radians)),
|
||||||
|
right,
|
||||||
|
float(np.degrees(top_radians)),
|
||||||
|
)
|
||||||
|
|
||||||
def min_(self) -> np.ndarray:
|
def min_(self) -> np.ndarray:
|
||||||
"""Get minimum coordinates."""
|
"""Get minimum coordinates."""
|
||||||
return np.array((self.bottom, self.left))
|
return np.array((self.bottom, self.left))
|
||||||
|
@ -113,6 +152,16 @@ class BoundaryBox:
|
||||||
<longitude 1>,<latitude 1>,<longitude 2>,<latitude 2>. Coordinates are
|
<longitude 1>,<latitude 1>,<longitude 2>,<latitude 2>. Coordinates are
|
||||||
rounded to three digits after comma.
|
rounded to three digits after comma.
|
||||||
"""
|
"""
|
||||||
return (
|
left: float = np.floor(self.left * 1000) / 1000
|
||||||
f"{self.left:.3f},{self.bottom:.3f},{self.right:.3f},{self.top:.3f}"
|
bottom: float = np.floor(self.bottom * 1000) / 1000
|
||||||
)
|
right: float = np.ceil(self.right * 1000) / 1000
|
||||||
|
top: float = np.ceil(self.top * 1000) / 1000
|
||||||
|
|
||||||
|
return f"{left:.3f},{bottom:.3f},{right:.3f},{top:.3f}"
|
||||||
|
|
||||||
|
def combine(self, other: "BoundaryBox") -> None:
|
||||||
|
"""Combine with another boundary box."""
|
||||||
|
self.left = min(self.left, other.left)
|
||||||
|
self.right = min(self.right, other.right)
|
||||||
|
self.bottom = min(self.bottom, other.bottom)
|
||||||
|
self.top = min(self.top, other.top)
|
||||||
|
|
|
@ -406,7 +406,7 @@ class DirectionSector(Tagged):
|
||||||
angle = float(self.get_tag("camera:angle"))
|
angle = float(self.get_tag("camera:angle"))
|
||||||
if "angle" in self.tags:
|
if "angle" in self.tags:
|
||||||
angle = float(self.get_tag("angle"))
|
angle = float(self.get_tag("angle"))
|
||||||
direction_radius = 25
|
direction_radius = 50
|
||||||
direction_color = scheme.get_color("direction_camera_color")
|
direction_color = scheme.get_color("direction_camera_color")
|
||||||
elif self.get_tag("traffic_sign") == "stop":
|
elif self.get_tag("traffic_sign") == "stop":
|
||||||
direction = self.get_tag("direction")
|
direction = self.get_tag("direction")
|
||||||
|
|
|
@ -6,7 +6,7 @@ import logging
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from map_machine.ui import parse_options
|
from map_machine.ui import parse_arguments
|
||||||
from map_machine.workspace import Workspace
|
from map_machine.workspace import Workspace
|
||||||
|
|
||||||
__author__ = "Sergey Vartanov"
|
__author__ = "Sergey Vartanov"
|
||||||
|
@ -18,7 +18,7 @@ def main() -> None:
|
||||||
logging.basicConfig(format="%(levelname)s %(message)s", level=logging.INFO)
|
logging.basicConfig(format="%(levelname)s %(message)s", level=logging.INFO)
|
||||||
workspace: Workspace = Workspace(Path("out"))
|
workspace: Workspace = Workspace(Path("out"))
|
||||||
|
|
||||||
arguments: argparse.Namespace = parse_options(sys.argv)
|
arguments: argparse.Namespace = parse_arguments(sys.argv)
|
||||||
|
|
||||||
if not arguments.command:
|
if not arguments.command:
|
||||||
logging.fatal("No command provided. See --help.")
|
logging.fatal("No command provided. See --help.")
|
||||||
|
|
|
@ -54,6 +54,7 @@ class MapConfiguration:
|
||||||
seed: str = ""
|
seed: str = ""
|
||||||
show_tooltips: bool = False
|
show_tooltips: bool = False
|
||||||
country: str = "world"
|
country: str = "world"
|
||||||
|
ignore_level_matching: bool = False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_options(
|
def from_options(
|
||||||
|
|
|
@ -20,7 +20,7 @@ from map_machine.flinger import Flinger
|
||||||
from map_machine.icon import ShapeExtractor
|
from map_machine.icon import ShapeExtractor
|
||||||
from map_machine.map_configuration import LabelMode, MapConfiguration
|
from map_machine.map_configuration import LabelMode, MapConfiguration
|
||||||
from map_machine.osm_getter import NetworkError, get_osm
|
from map_machine.osm_getter import NetworkError, get_osm
|
||||||
from map_machine.osm_reader import OSMData, OSMNode, OSMReader, OverpassReader
|
from map_machine.osm_reader import OSMData, OSMNode
|
||||||
from map_machine.point import Occupied, Point
|
from map_machine.point import Occupied, Point
|
||||||
from map_machine.road import Intersection, RoadPart
|
from map_machine.road import Intersection, RoadPart
|
||||||
from map_machine.scheme import Scheme
|
from map_machine.scheme import Scheme
|
||||||
|
@ -94,11 +94,11 @@ class Map:
|
||||||
for tree in constructor.craters:
|
for tree in constructor.craters:
|
||||||
tree.draw(self.svg, self.flinger)
|
tree.draw(self.svg, self.flinger)
|
||||||
|
|
||||||
|
self.draw_buildings(constructor)
|
||||||
|
|
||||||
for direction_sector in constructor.direction_sectors:
|
for direction_sector in constructor.direction_sectors:
|
||||||
direction_sector.draw(self.svg, self.scheme)
|
direction_sector.draw(self.svg, self.scheme)
|
||||||
|
|
||||||
self.draw_buildings(constructor)
|
|
||||||
|
|
||||||
# All other points
|
# All other points
|
||||||
|
|
||||||
occupied: Optional[Occupied]
|
occupied: Optional[Occupied]
|
||||||
|
@ -201,74 +201,73 @@ class Map:
|
||||||
intersection.draw(self.svg, True)
|
intersection.draw(self.svg, True)
|
||||||
|
|
||||||
|
|
||||||
def ui(options: argparse.Namespace) -> None:
|
def ui(arguments: argparse.Namespace) -> None:
|
||||||
"""
|
"""
|
||||||
Map Machine entry point.
|
Map Machine entry point.
|
||||||
|
|
||||||
:param options: command-line arguments
|
:param arguments: command-line arguments
|
||||||
"""
|
"""
|
||||||
configuration: MapConfiguration = MapConfiguration.from_options(
|
configuration: MapConfiguration = MapConfiguration.from_options(
|
||||||
options, int(options.zoom)
|
arguments, int(arguments.zoom)
|
||||||
)
|
)
|
||||||
if not options.boundary_box and not options.input_file_name:
|
cache_path: Path = Path(arguments.cache)
|
||||||
logging.fatal("Specify either --boundary-box, or --input.")
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
if options.boundary_box:
|
|
||||||
boundary_box: BoundaryBox = BoundaryBox.from_text(options.boundary_box)
|
|
||||||
|
|
||||||
cache_path: Path = Path(options.cache)
|
|
||||||
cache_path.mkdir(parents=True, exist_ok=True)
|
cache_path.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
input_file_names: list[Path]
|
boundary_box: Optional[BoundaryBox] = None
|
||||||
|
input_file_names: list[Path] = []
|
||||||
|
|
||||||
if options.input_file_name:
|
if arguments.input_file_names:
|
||||||
input_file_names = list(map(Path, options.input_file_name))
|
input_file_names = list(map(Path, arguments.input_file_names))
|
||||||
else:
|
else:
|
||||||
|
if arguments.boundary_box:
|
||||||
|
boundary_box = BoundaryBox.from_text(arguments.boundary_box)
|
||||||
|
else:
|
||||||
|
coordinates: np.ndarray = np.array(
|
||||||
|
list(map(float, arguments.coordinates.split(",")))
|
||||||
|
)
|
||||||
|
width, height = np.array(
|
||||||
|
list(map(float, arguments.size.split(",")))
|
||||||
|
)
|
||||||
|
boundary_box = BoundaryBox.from_coordinates(
|
||||||
|
coordinates, configuration.zoom_level, width, height
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cache_file_path: Path = (
|
cache_file_path: Path = (
|
||||||
cache_path / f"{boundary_box.get_format()}.osm"
|
cache_path / f"{boundary_box.get_format()}.osm"
|
||||||
)
|
)
|
||||||
get_osm(boundary_box, cache_file_path)
|
get_osm(boundary_box, cache_file_path)
|
||||||
|
input_file_names = [cache_file_path]
|
||||||
except NetworkError as e:
|
except NetworkError as e:
|
||||||
logging.fatal(e.message)
|
logging.fatal(e.message)
|
||||||
exit(1)
|
exit(1)
|
||||||
input_file_names = [cache_file_path]
|
|
||||||
|
|
||||||
scheme: Scheme = Scheme(workspace.DEFAULT_SCHEME_PATH)
|
scheme: Scheme = Scheme(workspace.DEFAULT_SCHEME_PATH)
|
||||||
min_: np.ndarray
|
min_: np.ndarray
|
||||||
max_: np.ndarray
|
max_: np.ndarray
|
||||||
osm_data: OSMData
|
osm_data: OSMData
|
||||||
view_box: BoundaryBox
|
|
||||||
|
|
||||||
if input_file_names[0].name.endswith(".json"):
|
osm_data: OSMData = OSMData()
|
||||||
reader: OverpassReader = OverpassReader()
|
|
||||||
reader.parse_json_file(input_file_names[0])
|
|
||||||
|
|
||||||
osm_data = reader.osm_data
|
for input_file_name in input_file_names:
|
||||||
view_box = boundary_box
|
if not input_file_name.is_file():
|
||||||
else:
|
logging.fatal(f"No such file: {input_file_name}.")
|
||||||
osm_reader = OSMReader()
|
|
||||||
|
|
||||||
for file_name in input_file_names:
|
|
||||||
if not file_name.is_file():
|
|
||||||
logging.fatal(f"No such file: {file_name}.")
|
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
osm_reader.parse_osm_file(file_name)
|
if input_file_name.name.endswith(".json"):
|
||||||
|
osm_data.parse_overpass(input_file_name)
|
||||||
osm_data = osm_reader.osm_data
|
|
||||||
|
|
||||||
if options.boundary_box:
|
|
||||||
view_box = boundary_box
|
|
||||||
else:
|
else:
|
||||||
view_box = osm_data.view_box
|
osm_data.parse_osm_file(input_file_name)
|
||||||
|
|
||||||
flinger: Flinger = Flinger(view_box, options.zoom, osm_data.equator_length)
|
view_box: BoundaryBox = boundary_box if boundary_box else osm_data.view_box
|
||||||
|
|
||||||
|
flinger: Flinger = Flinger(
|
||||||
|
view_box, arguments.zoom, osm_data.equator_length
|
||||||
|
)
|
||||||
size: np.ndarray = flinger.size
|
size: np.ndarray = flinger.size
|
||||||
|
|
||||||
svg: svgwrite.Drawing = svgwrite.Drawing(
|
svg: svgwrite.Drawing = svgwrite.Drawing(
|
||||||
options.output_file_name, size=size
|
arguments.output_file_name, size=size
|
||||||
)
|
)
|
||||||
icon_extractor: ShapeExtractor = ShapeExtractor(
|
icon_extractor: ShapeExtractor = ShapeExtractor(
|
||||||
workspace.ICONS_PATH, workspace.ICONS_CONFIG_PATH
|
workspace.ICONS_PATH, workspace.ICONS_CONFIG_PATH
|
||||||
|
@ -288,6 +287,6 @@ def ui(options: argparse.Namespace) -> None:
|
||||||
)
|
)
|
||||||
painter.draw(constructor)
|
painter.draw(constructor)
|
||||||
|
|
||||||
logging.info(f"Writing output SVG to {options.output_file_name}...")
|
logging.info(f"Writing output SVG to {arguments.output_file_name}...")
|
||||||
with open(options.output_file_name, "w") as output_file:
|
with open(arguments.output_file_name, "w") as output_file:
|
||||||
svg.write(output_file)
|
svg.write(output_file)
|
||||||
|
|
|
@ -355,119 +355,97 @@ class OSMData:
|
||||||
)
|
)
|
||||||
self.relations[relation.id_] = relation
|
self.relations[relation.id_] = relation
|
||||||
|
|
||||||
|
def parse_overpass(self, file_name: Path) -> None:
|
||||||
class OverpassReader:
|
|
||||||
"""
|
"""
|
||||||
Reader for JSON structure extracted from Overpass API.
|
Parse JSON structure extracted from Overpass API.
|
||||||
|
|
||||||
See https://wiki.openstreetmap.org/wiki/Overpass_API
|
See https://wiki.openstreetmap.org/wiki/Overpass_API
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self) -> None:
|
|
||||||
self.osm_data = OSMData()
|
|
||||||
|
|
||||||
def parse_json_file(self, file_name: Path) -> OSMData:
|
|
||||||
"""Parse JSON structure from the file and construct map."""
|
|
||||||
with file_name.open() as input_file:
|
with file_name.open() as input_file:
|
||||||
structure = json.load(input_file)
|
structure = json.load(input_file)
|
||||||
|
|
||||||
node_map = {}
|
node_map: dict[int, OSMNode] = {}
|
||||||
way_map = {}
|
way_map: dict[int, OSMWay] = {}
|
||||||
|
|
||||||
for element in structure["elements"]:
|
for element in structure["elements"]:
|
||||||
if element["type"] == "node":
|
if element["type"] == "node":
|
||||||
node = OSMNode.parse_from_structure(element)
|
node = OSMNode.parse_from_structure(element)
|
||||||
node_map[node.id_] = node
|
node_map[node.id_] = node
|
||||||
self.osm_data.add_node(node)
|
self.add_node(node)
|
||||||
|
|
||||||
for element in structure["elements"]:
|
for element in structure["elements"]:
|
||||||
if element["type"] == "way":
|
if element["type"] == "way":
|
||||||
way = OSMWay.parse_from_structure(element, node_map)
|
way = OSMWay.parse_from_structure(element, node_map)
|
||||||
way_map[way.id_] = way
|
way_map[way.id_] = way
|
||||||
self.osm_data.add_way(way)
|
self.add_way(way)
|
||||||
|
|
||||||
for element in structure["elements"]:
|
for element in structure["elements"]:
|
||||||
if element["type"] == "relation":
|
if element["type"] == "relation":
|
||||||
relation = OSMRelation.parse_from_structure(element)
|
relation = OSMRelation.parse_from_structure(element)
|
||||||
self.osm_data.add_relation(relation)
|
self.add_relation(relation)
|
||||||
|
|
||||||
return self.osm_data
|
def parse_osm_file(self, file_name: Path) -> None:
|
||||||
|
|
||||||
|
|
||||||
class OSMReader:
|
|
||||||
"""
|
|
||||||
OpenStreetMap XML file parser.
|
|
||||||
|
|
||||||
See https://wiki.openstreetmap.org/wiki/OSM_XML
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
parse_nodes: bool = True,
|
|
||||||
parse_ways: bool = True,
|
|
||||||
parse_relations: bool = True,
|
|
||||||
) -> None:
|
|
||||||
"""
|
|
||||||
:param parse_nodes: whether nodes should be parsed
|
|
||||||
:param parse_ways: whether ways should be parsed
|
|
||||||
:param parse_relations: whether relations should be parsed
|
|
||||||
"""
|
|
||||||
self.osm_data = OSMData()
|
|
||||||
self.parse_nodes: bool = parse_nodes
|
|
||||||
self.parse_ways: bool = parse_ways
|
|
||||||
self.parse_relations: bool = parse_relations
|
|
||||||
|
|
||||||
def parse_osm_file(self, file_name: Path) -> OSMData:
|
|
||||||
"""
|
"""
|
||||||
Parse OSM XML file.
|
Parse OSM XML file.
|
||||||
|
|
||||||
|
See https://wiki.openstreetmap.org/wiki/OSM_XML
|
||||||
|
|
||||||
:param file_name: input XML file
|
:param file_name: input XML file
|
||||||
:return: parsed map
|
:return: parsed map
|
||||||
"""
|
"""
|
||||||
return self.parse_osm(ElementTree.parse(file_name).getroot())
|
self.parse_osm(ElementTree.parse(file_name).getroot())
|
||||||
|
|
||||||
def parse_osm_text(self, text: str) -> OSMData:
|
def parse_osm_text(self, text: str) -> None:
|
||||||
"""
|
"""
|
||||||
Parse OSM XML data from text representation.
|
Parse OSM XML data from text representation.
|
||||||
|
|
||||||
:param text: XML text representation
|
:param text: XML text representation
|
||||||
:return: parsed map
|
:return: parsed map
|
||||||
"""
|
"""
|
||||||
return self.parse_osm(ElementTree.fromstring(text))
|
self.parse_osm(ElementTree.fromstring(text))
|
||||||
|
|
||||||
def parse_osm(self, root: Element) -> OSMData:
|
def parse_osm(
|
||||||
|
self,
|
||||||
|
root: Element,
|
||||||
|
parse_nodes: bool = True,
|
||||||
|
parse_ways: bool = True,
|
||||||
|
parse_relations: bool = True,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Parse OSM XML data.
|
Parse OSM XML data.
|
||||||
|
|
||||||
:param root: top element of XML data
|
:param root: top element of XML data
|
||||||
:return: parsed map
|
:param parse_nodes: whether nodes should be parsed
|
||||||
|
:param parse_ways: whether ways should be parsed
|
||||||
|
:param parse_relations: whether relations should be parsed
|
||||||
"""
|
"""
|
||||||
for element in root:
|
for element in root:
|
||||||
if element.tag == "bounds":
|
if element.tag == "bounds":
|
||||||
self.parse_bounds(element)
|
self.parse_bounds(element)
|
||||||
elif element.tag == "object":
|
elif element.tag == "object":
|
||||||
self.parse_object(element)
|
self.parse_object(element)
|
||||||
elif element.tag == "node" and self.parse_nodes:
|
elif element.tag == "node" and parse_nodes:
|
||||||
node = OSMNode.from_xml_structure(element)
|
node = OSMNode.from_xml_structure(element)
|
||||||
self.osm_data.add_node(node)
|
self.add_node(node)
|
||||||
elif element.tag == "way" and self.parse_ways:
|
elif element.tag == "way" and parse_ways:
|
||||||
self.osm_data.add_way(
|
self.add_way(OSMWay.from_xml_structure(element, self.nodes))
|
||||||
OSMWay.from_xml_structure(element, self.osm_data.nodes)
|
elif element.tag == "relation" and parse_relations:
|
||||||
)
|
self.add_relation(OSMRelation.from_xml_structure(element))
|
||||||
elif element.tag == "relation" and self.parse_relations:
|
|
||||||
self.osm_data.add_relation(
|
|
||||||
OSMRelation.from_xml_structure(element)
|
|
||||||
)
|
|
||||||
return self.osm_data
|
|
||||||
|
|
||||||
def parse_bounds(self, element: Element) -> None:
|
def parse_bounds(self, element: Element) -> None:
|
||||||
"""Parse view box from XML element."""
|
"""Parse view box from XML element."""
|
||||||
attributes = element.attrib
|
attributes = element.attrib
|
||||||
self.osm_data.view_box = BoundaryBox(
|
boundary_box: BoundaryBox = BoundaryBox(
|
||||||
float(attributes["minlon"]),
|
float(attributes["minlon"]),
|
||||||
float(attributes["minlat"]),
|
float(attributes["minlat"]),
|
||||||
float(attributes["maxlon"]),
|
float(attributes["maxlon"]),
|
||||||
float(attributes["maxlat"]),
|
float(attributes["maxlat"]),
|
||||||
)
|
)
|
||||||
|
if self.view_box:
|
||||||
|
self.view_box.combine(boundary_box)
|
||||||
|
else:
|
||||||
|
self.view_box = boundary_box
|
||||||
|
|
||||||
def parse_object(self, element: Element) -> None:
|
def parse_object(self, element: Element) -> None:
|
||||||
"""Parse astronomical object properties from XML element."""
|
"""Parse astronomical object properties from XML element."""
|
||||||
self.osm_data.equator_length = float(element.get("equator"))
|
self.equator_length = float(element.get("equator"))
|
||||||
|
|
|
@ -414,7 +414,10 @@ class Scheme:
|
||||||
continue
|
continue
|
||||||
if not matcher.is_matched(tags, configuration):
|
if not matcher.is_matched(tags, configuration):
|
||||||
continue
|
continue
|
||||||
if not matcher.check_zoom_level(configuration.zoom_level):
|
if (
|
||||||
|
not configuration.ignore_level_matching
|
||||||
|
and not matcher.check_zoom_level(configuration.zoom_level)
|
||||||
|
):
|
||||||
return None, 0
|
return None, 0
|
||||||
matcher_tags: set[str] = set(matcher.tags.keys())
|
matcher_tags: set[str] = set(matcher.tags.keys())
|
||||||
priority = len(self.node_matchers) - index
|
priority = len(self.node_matchers) - index
|
||||||
|
|
|
@ -29,8 +29,8 @@ COMMANDS: dict[str, list[str]] = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def parse_options(args: list[str]) -> argparse.Namespace:
|
def parse_arguments(args: list[str]) -> argparse.Namespace:
|
||||||
"""Parse Map Machine command-line options."""
|
"""Parse Map Machine command-line arguments."""
|
||||||
parser: argparse.ArgumentParser = argparse.ArgumentParser(
|
parser: argparse.ArgumentParser = argparse.ArgumentParser(
|
||||||
description="Map Machine. OpenStreetMap renderer with custom icon set"
|
description="Map Machine. OpenStreetMap renderer with custom icon set"
|
||||||
)
|
)
|
||||||
|
@ -203,7 +203,7 @@ def add_render_arguments(parser: argparse.ArgumentParser) -> None:
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-i",
|
"-i",
|
||||||
"--input",
|
"--input",
|
||||||
dest="input_file_name",
|
dest="input_file_names",
|
||||||
metavar="<path>",
|
metavar="<path>",
|
||||||
nargs="*",
|
nargs="*",
|
||||||
help="input XML file name or names (if not specified, file will be "
|
help="input XML file name or names (if not specified, file will be "
|
||||||
|
@ -238,6 +238,18 @@ def add_render_arguments(parser: argparse.ArgumentParser) -> None:
|
||||||
help="OSM zoom level",
|
help="OSM zoom level",
|
||||||
default=18,
|
default=18,
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-c",
|
||||||
|
"--coordinates",
|
||||||
|
metavar="<latitude>,<longitude>",
|
||||||
|
help="coordinates of any location inside the tile",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-s",
|
||||||
|
"--size",
|
||||||
|
metavar="<width>,<height>",
|
||||||
|
help="resulted image size",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def add_mapcss_arguments(parser: argparse.ArgumentParser) -> None:
|
def add_mapcss_arguments(parser: argparse.ArgumentParser) -> None:
|
||||||
|
|
Loading…
Add table
Reference in a new issue