Get use of map configuration.

This commit is contained in:
Sergey Vartanov 2021-08-27 00:40:00 +03:00
parent 56fdf9709e
commit 4c5209dabc
15 changed files with 160 additions and 130 deletions

View file

@ -36,7 +36,7 @@ Röntgen features\:
\3 {Isometric building shapes} {levels}
Isometric shapes for walls and shade in proportion to \osm {building:levels}, \osm {building:min_level}, \osm {height} and \osm {min_height} values.
With \m {--buildings isometric} or \m {--buildings isometric-no-parts} (not set by default), buildings are drawn using isometric shapes for walls and shade in proportion to \osm {building:levels}, \osm {building:min_level}, \osm {height} and \osm {min_height} values.
\image {doc/buildings.png} {3D buildings}
@ -265,5 +265,5 @@ To enable / disable Röntgen map paint style go to \kbd {View} → \kbd {Map Pai
Example of using Röntgen icons on top of Mapnik style. Map Paint Styles\:
\list
{✓ Mapnik}
{✓ Mapnik (true)}
{✓ Röntgen}

View file

@ -7,6 +7,9 @@ from dataclasses import dataclass
import numpy as np
__author__ = "Sergey Vartanov"
__email__ = "me@enzet.ru"
LATITUDE_MAX_DIFFERENCE: float = 0.5
LONGITUDE_MAX_DIFFERENCE: float = 0.5

View file

@ -1,7 +1,7 @@
"""
Color utility.
"""
from typing import Any, List
from typing import Any
from colour import Color
@ -22,7 +22,7 @@ def is_bright(color: Color) -> bool:
def get_gradient_color(
value: Any, bounds: MinMax, colors: List[Color]
value: Any, bounds: MinMax, colors: list[Color]
) -> Color:
"""
Get color from the color scale for the value.
@ -32,7 +32,7 @@ def get_gradient_color(
:param colors: color scale
"""
color_length: int = len(colors) - 1
scale: List[Color] = colors + [Color("black")]
scale: list[Color] = colors + [Color("black")]
range_coefficient: float = (
0 if bounds.is_empty() else (value - bounds.min_) / bounds.delta()

View file

@ -13,6 +13,7 @@ from roentgen import ui
from roentgen.color import get_gradient_color
from roentgen.figure import Building, DirectionSector, Road, StyledFigure, Tree
from roentgen.flinger import Flinger
from roentgen.map_configuration import DrawingMode, MapConfiguration
# fmt: off
from roentgen.icon import (
@ -21,9 +22,8 @@ from roentgen.icon import (
from roentgen.osm_reader import OSMData, OSMNode, OSMRelation, OSMWay
from roentgen.point import Point
from roentgen.scheme import DEFAULT_COLOR, LineStyle, Scheme
from roentgen.ui import AUTHOR_MODE, BuildingMode, TIME_MODE
from roentgen.ui import BuildingMode
from roentgen.util import MinMax
# fmt: on
__author__ = "Sergey Vartanov"
@ -113,7 +113,7 @@ def glue(ways: list[OSMWay]) -> list[list[OSMNode]]:
return result
def is_cycle(nodes) -> bool:
def is_cycle(nodes: list[OSMNode]) -> bool:
"""Is way a cycle way or an area boundary."""
return nodes[0] == nodes[-1]
@ -129,22 +129,22 @@ class Constructor:
flinger: Flinger,
scheme: Scheme,
icon_extractor: ShapeExtractor,
options,
configuration: MapConfiguration,
) -> None:
self.osm_data: OSMData = osm_data
self.flinger: Flinger = flinger
self.scheme: Scheme = scheme
self.icon_extractor = icon_extractor
self.options = options
self.configuration = configuration
if options.level:
if options.level == "overground":
if self.configuration.level:
if self.configuration.level == "overground":
self.check_level = check_level_overground
elif options.level == "underground":
elif self.configuration.level == "underground":
self.check_level = lambda x: not check_level_overground(x)
else:
self.check_level = lambda x: not check_level_number(
x, float(options.level)
x, float(self.configuration.level)
)
else:
self.check_level = lambda x: True
@ -203,10 +203,10 @@ class Constructor:
return
center_point, center_coordinates = line_center(outers[0], self.flinger)
if self.options.mode in [AUTHOR_MODE, TIME_MODE]:
if self.configuration.is_wireframe():
color: Color
if self.options.mode == AUTHOR_MODE:
color = get_user_color(line.user, self.options.seed)
if self.configuration.drawing_mode == DrawingMode.AUTHOR:
color = get_user_color(line.user, self.configuration.seed)
else: # self.mode == TIME_MODE
color = get_time_color(line.timestamp, self.osm_data.time)
self.draw_special_mode(inners, line, outers, color)
@ -215,7 +215,7 @@ class Constructor:
if not line.tags:
return
building_mode: BuildingMode = BuildingMode(self.options.buildings)
building_mode: BuildingMode = self.configuration.building_mode
if "building" in line.tags or (
building_mode == BuildingMode.ISOMETRIC
and "building:part" in line.tags
@ -352,13 +352,13 @@ class Constructor:
icon_set: IconSet
draw_outline: bool = True
if self.options.mode in [TIME_MODE, AUTHOR_MODE]:
if self.configuration.is_wireframe():
if not tags:
return
color: Color = DEFAULT_COLOR
if self.options.mode == AUTHOR_MODE:
color = get_user_color(node.user, self.options.seed)
if self.options.mode == TIME_MODE:
if self.configuration.drawing_mode == DrawingMode.AUTHOR:
color = get_user_color(node.user, self.configuration.seed)
if self.configuration.drawing_mode == DrawingMode.TIME:
color = get_time_color(node.timestamp, self.osm_data.time)
dot = self.icon_extractor.get_shape(DEFAULT_SMALL_SHAPE_ID)
icon_set = IconSet(

View file

@ -3,7 +3,7 @@ Drawing utility.
"""
from dataclasses import dataclass
from pathlib import Path
from typing import List, Optional, Union
from typing import Optional, Union
import cairo
import numpy as np
@ -73,7 +73,7 @@ class Drawing:
"""Draw rectangle."""
raise NotImplementedError
def line(self, points: List[np.ndarray], style: Style) -> None:
def line(self, points: list[np.ndarray], style: Style) -> None:
"""Draw line."""
raise NotImplementedError
@ -111,7 +111,7 @@ class SVGDrawing(Drawing):
style.update_svg_element(rectangle)
self.image.add(rectangle)
def line(self, points: List[np.ndarray], style: Style) -> None:
def line(self, points: list[np.ndarray], style: Style) -> None:
"""Draw line."""
commands: PathCommands = ["M"]
for point in points:
@ -159,7 +159,7 @@ class PNGDrawing(Drawing):
self.context.rectangle(point_1[0], point_1[1], size[0], size[1])
style.draw_png_stroke(self.context)
def line(self, points: List[np.ndarray], style: Style) -> None:
def line(self, points: list[np.ndarray], style: Style) -> None:
"""Draw line."""
if style.fill is not None:
self.context.move_to(float(points[0][0]), float(points[0][1]))

View file

@ -1,8 +1,8 @@
"""
Drawing separate map elements.
"""
import argparse
import logging
import sys
from pathlib import Path
import numpy as np
@ -13,15 +13,18 @@ from roentgen.point import Point
from roentgen.scheme import LineStyle, Scheme
from roentgen.workspace import workspace
__author__ = "Sergey Vartanov"
__email__ = "me@enzet.ru"
def draw_element(options) -> None:
def draw_element(options: argparse.Namespace) -> None:
"""Draw single node, line, or area."""
if options.node:
target: str = "node"
tags_description = options.node
else:
# Not implemented yet.
sys.exit(1)
exit(1)
tags: dict[str, str] = dict(
[x.split("=") for x in tags_description.split(",")]

View file

@ -1,7 +1,7 @@
"""
Figures displayed on the map.
"""
from typing import Any, Dict, List, Optional
from typing import Any, Optional
import numpy as np
from colour import Color
@ -25,15 +25,15 @@ class Figure(Tagged):
def __init__(
self,
tags: Dict[str, str],
inners: List[List[OSMNode]],
outers: List[List[OSMNode]],
tags: dict[str, str],
inners: list[list[OSMNode]],
outers: list[list[OSMNode]],
) -> None:
super().__init__()
self.tags: Dict[str, str] = tags
self.inners: List[List[OSMNode]] = []
self.outers: List[List[OSMNode]] = []
self.tags: dict[str, str] = tags
self.inners: list[list[OSMNode]] = []
self.outers: list[list[OSMNode]] = []
for inner_nodes in inners:
self.inners.append(make_clockwise(inner_nodes))
@ -67,15 +67,15 @@ class Building(Figure):
def __init__(
self,
tags: Dict[str, str],
inners: List[List[OSMNode]],
outers: List[List[OSMNode]],
tags: dict[str, str],
inners: list[list[OSMNode]],
outers: list[list[OSMNode]],
flinger: Flinger,
scheme: Scheme,
) -> None:
super().__init__(tags, inners, outers)
style: Dict[str, Any] = {
style: dict[str, Any] = {
"fill": scheme.get_color("building_color").hex,
"stroke": scheme.get_color("building_border_color").hex,
}
@ -195,9 +195,9 @@ class StyledFigure(Figure):
def __init__(
self,
tags: Dict[str, str],
inners: List[List[OSMNode]],
outers: List[List[OSMNode]],
tags: dict[str, str],
inners: list[list[OSMNode]],
outers: list[list[OSMNode]],
line_style: LineStyle,
) -> None:
super().__init__(tags, inners, outers)
@ -211,16 +211,16 @@ class Road(Figure):
def __init__(
self,
tags: Dict[str, str],
inners: List[List[OSMNode]],
outers: List[List[OSMNode]],
tags: dict[str, str],
inners: list[list[OSMNode]],
outers: list[list[OSMNode]],
matcher: RoadMatcher,
) -> None:
super().__init__(tags, inners, outers)
self.matcher: RoadMatcher = matcher
self.width: Optional[float] = None
self.lanes: List[Lane] = []
self.lanes: list[Lane] = []
if "lanes" in tags:
try:
@ -359,7 +359,7 @@ class Segment:
) # fmt: skip
def is_clockwise(polygon: List[OSMNode]) -> bool:
def is_clockwise(polygon: list[OSMNode]) -> bool:
"""
Return true if polygon nodes are in clockwise order.
@ -374,7 +374,7 @@ def is_clockwise(polygon: List[OSMNode]) -> bool:
return count >= 0
def make_clockwise(polygon: List[OSMNode]) -> List[OSMNode]:
def make_clockwise(polygon: list[OSMNode]) -> list[OSMNode]:
"""
Make polygon nodes clockwise.
@ -383,7 +383,7 @@ def make_clockwise(polygon: List[OSMNode]) -> List[OSMNode]:
return polygon if is_clockwise(polygon) else list(reversed(polygon))
def make_counter_clockwise(polygon: List[OSMNode]) -> List[OSMNode]:
def make_counter_clockwise(polygon: list[OSMNode]) -> list[OSMNode]:
"""
Make polygon nodes counter-clockwise.
@ -392,7 +392,7 @@ def make_counter_clockwise(polygon: List[OSMNode]) -> List[OSMNode]:
return polygon if not is_clockwise(polygon) else list(reversed(polygon))
def get_path(nodes: List[OSMNode], shift: np.array, flinger: Flinger) -> str:
def get_path(nodes: list[OSMNode], shift: np.array, flinger: Flinger) -> str:
"""Construct SVG path commands from nodes."""
path: str = ""
prev_node: Optional[OSMNode] = None

View file

@ -34,6 +34,7 @@ class IconCollection:
background_color: Color = Color("white"),
color: Color = Color("black"),
add_unused: bool = False,
add_all: bool = False,
) -> "IconCollection":
"""
Collect all possible icon combinations in grid.
@ -44,6 +45,7 @@ class IconCollection:
:param color: icon color
:param add_unused: create icons from shapes that have no corresponding
tags
:param add_all: create icons from all possible shapes including parts
"""
icons: list[Icon] = []
@ -111,6 +113,13 @@ class IconCollection:
icon.recolor(color)
icons.append(icon)
if add_all:
for shape_id in extractor.shapes.keys():
shape: Shape = extractor.get_shape(shape_id)
icon: Icon = Icon([ShapeSpecification(shape)])
icon.recolor(color)
icons.append(icon)
return cls(icons)
def draw_icons(
@ -204,8 +213,9 @@ def draw_icons() -> None:
extractor: ShapeExtractor = ShapeExtractor(
workspace.ICONS_PATH, workspace.ICONS_CONFIG_PATH
)
collection: IconCollection = IconCollection.from_scheme(scheme, extractor)
collection: IconCollection = IconCollection.from_scheme(
scheme, extractor, add_all=True
)
icon_grid_path: Path = workspace.get_icon_grid_path()
collection.draw_grid(icon_grid_path)
logging.info(f"Icon grid is written to {icon_grid_path}.")

View file

@ -322,7 +322,7 @@ class ShapeSpecification:
def draw(
self,
svg,
svg: Drawing,
point: np.array,
tags: dict[str, Any] = None,
outline: bool = False,
@ -372,7 +372,7 @@ class ShapeSpecification:
and np.allclose(self.offset, other.offset)
)
def __lt__(self, other) -> bool:
def __lt__(self, other: "ShapeSpecification") -> bool:
return self.shape.id_ < other.shape.id_
@ -471,12 +471,12 @@ class Icon:
"""Add shape specifications to the icon."""
self.shape_specifications += specifications
def __eq__(self, other) -> bool:
def __eq__(self, other: "Icon") -> bool:
return sorted(self.shape_specifications) == sorted(
other.shape_specifications
)
def __lt__(self, other) -> bool:
def __lt__(self, other: "Icon") -> bool:
return "".join(
[x.shape.id_ for x in self.shape_specifications]
) < "".join([x.shape.id_ for x in other.shape_specifications])

View file

@ -1,6 +1,7 @@
"""
MapCSS scheme creation.
"""
import argparse
import logging
from pathlib import Path
from typing import Optional, TextIO
@ -175,7 +176,7 @@ class MapCSSWriter:
)
def ui(options) -> None:
def ui(options: argparse.Namespace) -> None:
"""Write MapCSS 0.2 scheme."""
directory: Path = workspace.get_mapcss_path()
icons_with_outline_path: Path = workspace.get_mapcss_icons_path()

View file

@ -1,6 +1,7 @@
"""
Simple OpenStreetMap renderer.
"""
import argparse
import logging
from pathlib import Path
from typing import Any, Iterator
@ -17,12 +18,13 @@ from roentgen.constructor import Constructor
from roentgen.figure import Road
from roentgen.flinger import Flinger
from roentgen.icon import ShapeExtractor
from roentgen.map_configuration import LabelMode, MapConfiguration
from roentgen.osm_getter import NetworkError, get_osm
from roentgen.osm_reader import OSMData, OSMNode, OSMReader, OverpassReader
from roentgen.point import Occupied
from roentgen.road import Intersection, RoadPart
from roentgen.scheme import Scheme
from roentgen.ui import AUTHOR_MODE, BuildingMode, TIME_MODE, progress_bar
from roentgen.ui import BuildingMode, progress_bar
from roentgen.workspace import workspace
__author__ = "Sergey Vartanov"
@ -35,15 +37,19 @@ class Map:
"""
def __init__(
self, flinger: Flinger, svg: svgwrite.Drawing, scheme: Scheme, options
self,
flinger: Flinger,
svg: svgwrite.Drawing,
scheme: Scheme,
configuration: MapConfiguration,
) -> None:
self.flinger: Flinger = flinger
self.svg: svgwrite.Drawing = svg
self.scheme: Scheme = scheme
self.options = options
self.configuration = configuration
self.background_color: Color = self.scheme.get_color("background_color")
if self.options.mode in [AUTHOR_MODE, TIME_MODE]:
if self.configuration.is_wireframe():
self.background_color: Color = Color("#111111")
def draw(self, constructor: Constructor) -> None:
@ -80,11 +86,13 @@ class Map:
# All other points
if self.options.overlap == 0:
if self.configuration.overlap == 0:
occupied = None
else:
occupied = Occupied(
self.flinger.size[0], self.flinger.size[1], self.options.overlap
self.flinger.size[0],
self.flinger.size[1],
self.configuration.overlap,
)
nodes = sorted(constructor.points, key=lambda x: -x.priority)
@ -105,18 +113,18 @@ class Map:
steps * 2 + index, steps * 3, step=10, text="Drawing texts"
)
if (
self.options.mode not in [TIME_MODE, AUTHOR_MODE]
and self.options.label_mode != "no"
not self.configuration.is_wireframe()
and self.configuration.label_mode != LabelMode.NO
):
point.draw_texts(self.svg, occupied, self.options.label_mode)
point.draw_texts(
self.svg, occupied, self.configuration.label_mode
)
progress_bar(-1, len(nodes), step=10, text="Drawing nodes")
def draw_buildings(self, constructor: Constructor) -> None:
"""Draw buildings: shade, walls, and roof."""
building_mode: BuildingMode = BuildingMode(self.options.buildings)
if building_mode == BuildingMode.FLAT:
if self.configuration.building_mode == BuildingMode.FLAT:
for building in constructor.buildings:
building.draw(self.svg, self.flinger)
return
@ -197,12 +205,14 @@ class Map:
intersection.draw(self.svg, True)
def ui(options) -> None:
def ui(options: argparse.Namespace) -> None:
"""
Röntgen entry point.
:param options: command-line arguments
"""
configuration: MapConfiguration = MapConfiguration.from_options(options)
if not options.boundary_box and not options.input_file_name:
logging.fatal("Specify either --boundary-box, or --input.")
exit(1)
@ -241,8 +251,7 @@ def ui(options) -> None:
osm_data = reader.osm_data
view_box = boundary_box
else:
is_full: bool = options.mode in [AUTHOR_MODE, TIME_MODE]
osm_reader = OSMReader(is_full=is_full)
osm_reader = OSMReader(is_full=configuration.is_wireframe())
for file_name in input_file_names:
if not file_name.is_file():
@ -273,11 +282,13 @@ def ui(options) -> None:
flinger=flinger,
scheme=scheme,
icon_extractor=icon_extractor,
options=options,
configuration=configuration,
)
constructor.construct()
painter: Map = Map(flinger=flinger, svg=svg, scheme=scheme, options=options)
painter: Map = Map(
flinger=flinger, svg=svg, scheme=scheme, configuration=configuration
)
painter.draw(constructor)
logging.info(f"Writing output SVG to {options.output_file_name}...")

View file

@ -8,6 +8,7 @@ import svgwrite
from colour import Color
from roentgen.icon import Icon, IconSet
from roentgen.map_configuration import LabelMode
from roentgen.osm_reader import Tagged
from roentgen.text import Label
@ -161,14 +162,14 @@ class Point(Tagged):
self,
svg: svgwrite.Drawing,
occupied: Optional[Occupied] = None,
label_mode: str = "main",
label_mode: LabelMode = LabelMode.MAIN,
) -> None:
"""Draw all labels."""
labels: list[Label]
if label_mode == "main":
if label_mode == LabelMode.MAIN:
labels = self.labels[:1]
elif label_mode == "all":
elif label_mode == LabelMode.ALL:
labels = self.labels
else:
return

View file

@ -1,6 +1,7 @@
"""
Röntgen tile server for slippy maps.
"""
import argparse
import logging
from http.server import HTTPServer, SimpleHTTPRequestHandler
from pathlib import Path
@ -62,7 +63,7 @@ class _Handler(SimpleHTTPRequestHandler):
return
def ui(options) -> None:
def ui(options: argparse.Namespace) -> None:
"""Command-line interface for tile server."""
server: Optional[HTTPServer] = None
try:

View file

@ -3,6 +3,7 @@ Tile generation.
See https://wiki.openstreetmap.org/wiki/Tiles
"""
import argparse
import logging
import sys
from dataclasses import dataclass
@ -18,6 +19,7 @@ from roentgen.constructor import Constructor
from roentgen.flinger import Flinger
from roentgen.icon import ShapeExtractor
from roentgen.mapper import Map
from roentgen.map_configuration import MapConfiguration
from roentgen.osm_getter import NetworkError, get_osm
from roentgen.osm_reader import OSMData, OSMReader
from roentgen.scheme import Scheme
@ -115,23 +117,31 @@ class Tile:
f"https://tile.openstreetmap.org/{self.scale}/{self.x}/{self.y}.png"
)
def draw(self, directory_name: Path, cache_path: Path, options) -> None:
def draw(
self,
directory_name: Path,
cache_path: Path,
configuration: MapConfiguration,
) -> None:
"""
Draw tile to SVG and PNG files.
:param directory_name: output directory to storing tiles
:param cache_path: directory to store SVG and PNG tiles
:param options: drawing configuration
:param configuration: drawing configuration
"""
try:
osm_data: OSMData = self.load_osm_data(cache_path)
except NetworkError as e:
raise NetworkError(f"Map is not loaded. {e.message}")
self.draw_with_osm_data(osm_data, directory_name, options)
self.draw_with_osm_data(osm_data, directory_name, configuration)
def draw_with_osm_data(
self, osm_data: OSMData, directory_name: Path, options
self,
osm_data: OSMData,
directory_name: Path,
configuration: MapConfiguration,
) -> None:
"""Draw SVG and PNG tile using OpenStreetMap data."""
top, left = self.get_coordinates()
@ -154,12 +164,12 @@ class Tile:
)
scheme: Scheme = Scheme(workspace.DEFAULT_SCHEME_PATH)
constructor: Constructor = Constructor(
osm_data, flinger, scheme, icon_extractor, options
osm_data, flinger, scheme, icon_extractor, configuration
)
constructor.construct()
painter: Map = Map(
flinger=flinger, svg=svg, scheme=scheme, options=options
flinger=flinger, svg=svg, scheme=scheme, configuration=configuration
)
painter.draw(constructor)
@ -216,7 +226,7 @@ class Tiles:
return cls(tiles, tile_1, tile_2, scale, extended_boundary_box)
def draw_separately(
self, directory: Path, cache_path: Path, options
self, directory: Path, cache_path: Path, configuration: MapConfiguration
) -> None:
"""
Draw set of tiles as SVG file separately and rasterize them into a set
@ -224,7 +234,7 @@ class Tiles:
:param directory: directory for tiles
:param cache_path: directory for temporary OSM files
:param options: drawing configuration
:param configuration: drawing configuration
"""
cache_file_path: Path = (
cache_path / f"{self.boundary_box.get_format()}.osm"
@ -235,7 +245,7 @@ class Tiles:
for tile in self.tiles:
file_path: Path = tile.get_file_name(directory)
if not file_path.exists():
tile.draw_with_osm_data(osm_data, directory, options)
tile.draw_with_osm_data(osm_data, directory, configuration)
else:
logging.debug(f"File {file_path} already exists.")
@ -253,19 +263,21 @@ class Tiles:
"""Check whether all tiles are drawn."""
return all(x.exists(directory_name) for x in self.tiles)
def draw(self, directory: Path, cache_path: Path, options) -> None:
def draw(
self, directory: Path, cache_path: Path, configuration: MapConfiguration
) -> None:
"""
Draw one PNG image with all tiles and split it into a set of separate
PNG file with Pillow.
:param directory: directory for tiles
:param cache_path: directory for temporary OSM files
:param options: drawing configuration
:param configuration: drawing configuration
"""
if self.tiles_exist(directory):
return
self.draw_image(cache_path, options)
self.draw_image(cache_path, configuration)
input_path: Path = self.get_file_path(cache_path).with_suffix(".png")
with input_path.open("rb") as input_file:
@ -290,12 +302,14 @@ class Tiles:
"""Get path of the output SVG file."""
return cache_path / f"{self.boundary_box.get_format()}_{self.scale}.svg"
def draw_image(self, cache_path: Path, options) -> None:
def draw_image(
self, cache_path: Path, configuration: MapConfiguration
) -> None:
"""
Draw all tiles as one picture.
:param cache_path: directory for temporary SVG file and OSM files
:param options: drawing configuration
:param configuration: drawing configuration
"""
output_path: Path = self.get_file_path(cache_path)
@ -319,16 +333,14 @@ class Tiles:
)
scheme: Scheme = Scheme(workspace.DEFAULT_SCHEME_PATH)
constructor: Constructor = Constructor(
osm_data, flinger, scheme, extractor, options=options
osm_data, flinger, scheme, extractor, configuration
)
constructor.construct()
svg: svgwrite.Drawing = svgwrite.Drawing(
str(output_path), size=flinger.size
)
map_: Map = Map(
flinger=flinger, svg=svg, scheme=scheme, options=options
)
map_: Map = Map(flinger, svg, scheme, configuration)
map_.draw(constructor)
logging.info(f"Writing output SVG {output_path}...")
@ -346,9 +358,10 @@ class Tiles:
logging.debug(f"File {png_path} already exists.")
def ui(options) -> None:
def ui(options: argparse.Namespace) -> None:
"""Simple user interface for tile generation."""
directory: Path = workspace.get_tile_path()
configuration: MapConfiguration = MapConfiguration.from_options(options)
if options.coordinates:
coordinates: list[float] = list(
@ -356,13 +369,13 @@ def ui(options) -> None:
)
tile: Tile = Tile.from_coordinates(np.array(coordinates), options.scale)
try:
tile.draw(directory, Path(options.cache), options)
tile.draw(directory, Path(options.cache), configuration)
except NetworkError as e:
logging.fatal(e.message)
elif options.tile:
scale, x, y = map(int, options.tile.split("/"))
tile: Tile = Tile(x, y, scale)
tile.draw(directory, Path(options.cache), options)
tile.draw(directory, Path(options.cache), configuration)
elif options.boundary_box:
boundary_box: Optional[BoundaryBox] = BoundaryBox.from_text(
options.boundary_box
@ -370,7 +383,7 @@ def ui(options) -> None:
if boundary_box is None:
sys.exit(1)
tiles: Tiles = Tiles.from_boundary_box(boundary_box, options.scale)
tiles.draw(directory, Path(options.cache), options)
tiles.draw(directory, Path(options.cache), configuration)
else:
logging.fatal(
"Specify either --coordinates, --boundary-box, or --tile."

View file

@ -4,29 +4,15 @@ Command-line user interface.
import argparse
import sys
from roentgen.map_configuration import BuildingMode, DrawingMode, LabelMode
from roentgen.osm_reader import STAGES_OF_DECAY
__author__ = "Sergey Vartanov"
__email__ = "me@enzet.ru"
from enum import Enum
from roentgen.osm_reader import STAGES_OF_DECAY
BOXES: str = " ▏▎▍▌▋▊▉"
BOXES_LENGTH: int = len(BOXES)
AUTHOR_MODE: str = "author"
TIME_MODE: str = "time"
class BuildingMode(Enum):
"""
Building drawing mode.
"""
FLAT = "flat"
ISOMETRIC = "isometric"
ISOMETRIC_NO_PARTS = "isometric-no-parts"
def parse_options(args) -> argparse.Namespace:
"""Parse Röntgen command-line options."""
@ -68,8 +54,9 @@ def add_map_arguments(parser: argparse.ArgumentParser) -> None:
parser.add_argument(
"--mode",
default="normal",
help="map drawing mode",
metavar="<string>",
choices=(x.value for x in DrawingMode),
help="map drawing mode: " + ", ".join(x.value for x in DrawingMode),
)
parser.add_argument(
"--overlap",
@ -81,10 +68,11 @@ def add_map_arguments(parser: argparse.ArgumentParser) -> None:
)
parser.add_argument(
"--labels",
help="label drawing mode: `no`, `main`, or `all`",
dest="label_mode",
default="main",
metavar="<string>",
choices=(x.value for x in LabelMode),
help="label drawing mode: " + ", ".join(x.value for x in LabelMode),
)
parser.add_argument(
"-s",
@ -99,6 +87,12 @@ def add_map_arguments(parser: argparse.ArgumentParser) -> None:
default="overground",
help="display only this floor level",
)
parser.add_argument(
"--seed",
default="",
help="seed for random",
metavar="<string>",
)
def add_tile_arguments(parser: argparse.ArgumentParser) -> None:
@ -179,12 +173,6 @@ def add_render_arguments(parser: argparse.ArgumentParser) -> None:
default="cache",
metavar="<path>",
)
parser.add_argument(
"--seed",
default="",
help="seed for random",
metavar="<string>",
)
def add_mapcss_arguments(parser: argparse.ArgumentParser) -> None:
@ -225,14 +213,13 @@ def progress_bar(
:param text: short description
"""
if number == -1:
print(f"100 % {length * ''}{text}")
sys.stdout.write(f"100 % {length * ''}{text}\n")
elif number % step == 0:
ratio: float = number / total
parts: int = int(ratio * length * BOXES_LENGTH)
fill_length: int = int(parts / BOXES_LENGTH)
box: str = BOXES[int(parts - fill_length * BOXES_LENGTH)]
print(
sys.stdout.write(
f"{str(int(int(ratio * 1000) / 10)):>3} % {fill_length * ''}{box}"
f"{int(length - fill_length - 1) * ' '}{text}"
f"{int(length - fill_length - 1) * ' '}{text}\n\033[F"
)
sys.stdout.write("\033[F")