mirror of
https://github.com/enzet/map-machine.git
synced 2025-05-21 04:56:24 +02:00
Issue #88: backport to Python 3.8.
This commit is contained in:
parent
e9b1b499bf
commit
75128537da
30 changed files with 303 additions and 267 deletions
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
|
@ -11,10 +11,10 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python 3.9
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.9
|
||||
python-version: 3.8
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
|
|
|
@ -120,7 +120,7 @@ Every way and node displayed with the random color picked for each author with `
|
|||
Installation
|
||||
------------
|
||||
|
||||
Requirements: Python 3.9.
|
||||
Requirements: Python 3.8.
|
||||
|
||||
To install all packages, run:
|
||||
|
||||
|
|
|
@ -13,5 +13,5 @@ echo "Lint with Flake8..."
|
|||
flake8 \
|
||||
--max-line-length=80 \
|
||||
--ignore=E203,W503,ANN002,ANN003,ANN101,ANN102 \
|
||||
--exclude=work,precommit.py,tests/test_road.py \
|
||||
--exclude=work,precommit.py,tests/test_road.py,python3.8 \
|
||||
|| { echo "FAIL"; exit 1; }
|
||||
|
|
|
@ -168,7 +168,7 @@ Every way and node displayed with the random color picked for each author with \
|
|||
|
||||
\2 {Installation} {installation}
|
||||
|
||||
Requirements\: Python 3.9/* or higher*/.
|
||||
Requirements\: Python 3.8/* or higher*/.
|
||||
|
||||
To install all packages, run\:
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
"""
|
||||
Color utility.
|
||||
"""
|
||||
from typing import Any
|
||||
from typing import Any, List
|
||||
|
||||
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()
|
||||
|
|
|
@ -4,7 +4,7 @@ Construct Map Machine nodes and ways.
|
|||
import logging
|
||||
from datetime import datetime
|
||||
from hashlib import sha256
|
||||
from typing import Any, Iterator, Optional, Union
|
||||
from typing import Any, Dict, Iterator, List, Optional, Set, Tuple, Union
|
||||
|
||||
import numpy as np
|
||||
from colour import Color
|
||||
|
@ -46,7 +46,7 @@ __author__ = "Sergey Vartanov"
|
|||
__email__ = "me@enzet.ru"
|
||||
|
||||
DEBUG: bool = False
|
||||
TIME_COLOR_SCALE: list[Color] = [
|
||||
TIME_COLOR_SCALE: List[Color] = [
|
||||
Color("#581845"),
|
||||
Color("#900C3F"),
|
||||
Color("#C70039"),
|
||||
|
@ -57,7 +57,7 @@ TIME_COLOR_SCALE: list[Color] = [
|
|||
|
||||
|
||||
def line_center(
|
||||
nodes: list[OSMNode], flinger: Flinger
|
||||
nodes: List[OSMNode], flinger: Flinger
|
||||
) -> (np.ndarray, np.ndarray):
|
||||
"""
|
||||
Get geometric center of nodes set.
|
||||
|
@ -65,7 +65,7 @@ def line_center(
|
|||
:param nodes: node list
|
||||
:param flinger: flinger that remap geo positions
|
||||
"""
|
||||
boundary: list[MinMax] = [MinMax(), MinMax()]
|
||||
boundary: List[MinMax] = [MinMax(), MinMax()]
|
||||
|
||||
for node in nodes:
|
||||
boundary[0].update(node.coordinates[0])
|
||||
|
@ -95,14 +95,14 @@ def get_time_color(time: Optional[datetime], boundaries: MinMax) -> Color:
|
|||
return get_gradient_color(time, boundaries, TIME_COLOR_SCALE)
|
||||
|
||||
|
||||
def glue(ways: list[OSMWay]) -> list[list[OSMNode]]:
|
||||
def glue(ways: List[OSMWay]) -> List[List[OSMNode]]:
|
||||
"""
|
||||
Try to glue ways that share nodes.
|
||||
|
||||
:param ways: ways to glue
|
||||
"""
|
||||
result: list[list[OSMNode]] = []
|
||||
to_process: set[tuple[OSMNode]] = set()
|
||||
result: List[List[OSMNode]] = []
|
||||
to_process: Set[Tuple[OSMNode]] = set()
|
||||
|
||||
for way in ways:
|
||||
if way.is_cycle():
|
||||
|
@ -111,9 +111,9 @@ def glue(ways: list[OSMWay]) -> list[list[OSMNode]]:
|
|||
to_process.add(tuple(way.nodes))
|
||||
|
||||
while to_process:
|
||||
nodes: list[OSMNode] = list(to_process.pop())
|
||||
glued: Optional[list[OSMNode]] = None
|
||||
other_nodes: Optional[tuple[OSMNode]] = None
|
||||
nodes: List[OSMNode] = list(to_process.pop())
|
||||
glued: Optional[List[OSMNode]] = None
|
||||
other_nodes: Optional[Tuple[OSMNode]] = None
|
||||
|
||||
for other_nodes in to_process:
|
||||
glued = try_to_glue(nodes, list(other_nodes))
|
||||
|
@ -132,14 +132,14 @@ def glue(ways: list[OSMWay]) -> list[list[OSMNode]]:
|
|||
return result
|
||||
|
||||
|
||||
def is_cycle(nodes: list[OSMNode]) -> bool:
|
||||
def is_cycle(nodes: List[OSMNode]) -> bool:
|
||||
"""Is way a cycle way or an area boundary."""
|
||||
return nodes[0] == nodes[-1]
|
||||
|
||||
|
||||
def try_to_glue(
|
||||
nodes: list[OSMNode], other: list[OSMNode]
|
||||
) -> Optional[list[OSMNode]]:
|
||||
nodes: List[OSMNode], other: List[OSMNode]
|
||||
) -> Optional[List[OSMNode]]:
|
||||
"""Create new combined way if ways share endpoints."""
|
||||
if nodes[0] == other[0]:
|
||||
return list(reversed(other[1:])) + nodes
|
||||
|
@ -182,15 +182,15 @@ class Constructor:
|
|||
x, float(self.configuration.level)
|
||||
)
|
||||
|
||||
self.points: list[Point] = []
|
||||
self.figures: list[StyledFigure] = []
|
||||
self.buildings: list[Building] = []
|
||||
self.points: List[Point] = []
|
||||
self.figures: List[StyledFigure] = []
|
||||
self.buildings: List[Building] = []
|
||||
self.roads: Roads = Roads()
|
||||
self.trees: list[Tree] = []
|
||||
self.craters: list[Crater] = []
|
||||
self.direction_sectors: list[DirectionSector] = []
|
||||
self.trees: List[Tree] = []
|
||||
self.craters: List[Crater] = []
|
||||
self.direction_sectors: List[DirectionSector] = []
|
||||
|
||||
self.heights: set[float] = {2, 4}
|
||||
self.heights: Set[float] = {2, 4}
|
||||
|
||||
def add_building(self, building: Building) -> None:
|
||||
"""Add building and update levels."""
|
||||
|
@ -221,8 +221,8 @@ class Constructor:
|
|||
def construct_line(
|
||||
self,
|
||||
line: Union[OSMWay, OSMRelation],
|
||||
inners: list[list[OSMNode]],
|
||||
outers: list[list[OSMNode]],
|
||||
inners: List[List[OSMNode]],
|
||||
outers: List[List[OSMNode]],
|
||||
) -> None:
|
||||
"""
|
||||
Way or relation construction.
|
||||
|
@ -265,7 +265,7 @@ class Constructor:
|
|||
)
|
||||
return
|
||||
|
||||
processed: set[str] = set()
|
||||
processed: Set[str] = set()
|
||||
|
||||
recolor: Optional[Color] = None
|
||||
|
||||
|
@ -275,11 +275,11 @@ class Constructor:
|
|||
recolor = self.scheme.get_color(line.tags[color_tag_key])
|
||||
processed.add(color_tag_key)
|
||||
|
||||
line_styles: list[LineStyle] = self.scheme.get_style(line.tags)
|
||||
line_styles: List[LineStyle] = self.scheme.get_style(line.tags)
|
||||
|
||||
for line_style in line_styles:
|
||||
if recolor is not None:
|
||||
new_style: dict[str, Union[float, int, str]] = dict(
|
||||
new_style: Dict[str, Union[float, int, str]] = dict(
|
||||
line_style.style
|
||||
)
|
||||
new_style["stroke"] = recolor.hex
|
||||
|
@ -302,7 +302,7 @@ class Constructor:
|
|||
self.extractor, line.tags, processed, self.configuration
|
||||
)
|
||||
if icon_set is not None:
|
||||
labels: list[Label] = self.scheme.construct_text(
|
||||
labels: List[Label] = self.scheme.construct_text(
|
||||
line.tags, "all", processed
|
||||
)
|
||||
point: Point = Point(
|
||||
|
@ -319,7 +319,7 @@ class Constructor:
|
|||
|
||||
if not line_styles:
|
||||
if DEBUG:
|
||||
style: dict[str, Any] = {
|
||||
style: Dict[str, Any] = {
|
||||
"fill": "none",
|
||||
"stroke": Color("red").hex,
|
||||
"stroke-width": 1,
|
||||
|
@ -329,7 +329,7 @@ class Constructor:
|
|||
)
|
||||
self.figures.append(figure)
|
||||
|
||||
processed: set[str] = set()
|
||||
processed: Set[str] = set()
|
||||
|
||||
priority: int
|
||||
icon_set: IconSet
|
||||
|
@ -340,7 +340,7 @@ class Constructor:
|
|||
self.configuration,
|
||||
)
|
||||
if icon_set is not None:
|
||||
labels: list[Label] = self.scheme.construct_text(
|
||||
labels: List[Label] = self.scheme.construct_text(
|
||||
line.tags, "all", processed
|
||||
)
|
||||
point: Point = Point(
|
||||
|
@ -358,12 +358,12 @@ class Constructor:
|
|||
def draw_special_mode(
|
||||
self,
|
||||
line: Union[OSMWay, OSMRelation],
|
||||
inners: list[list[OSMNode]],
|
||||
outers: list[list[OSMNode]],
|
||||
inners: List[List[OSMNode]],
|
||||
outers: List[List[OSMNode]],
|
||||
color: Color,
|
||||
) -> None:
|
||||
"""Add figure for special mode: time or author."""
|
||||
style: dict[str, Any] = {
|
||||
style: Dict[str, Any] = {
|
||||
"fill": "none",
|
||||
"stroke": color.hex,
|
||||
"stroke-width": 1,
|
||||
|
@ -376,13 +376,13 @@ class Constructor:
|
|||
"""Construct Map Machine ways from OSM relations."""
|
||||
for relation_id in self.osm_data.relations:
|
||||
relation: OSMRelation = self.osm_data.relations[relation_id]
|
||||
tags: dict[str, str] = relation.tags
|
||||
tags: Dict[str, str] = relation.tags
|
||||
if not self.check_level(tags):
|
||||
continue
|
||||
if "type" not in tags or tags["type"] != "multipolygon":
|
||||
continue
|
||||
inner_ways: list[OSMWay] = []
|
||||
outer_ways: list[OSMWay] = []
|
||||
inner_ways: List[OSMWay] = []
|
||||
outer_ways: List[OSMWay] = []
|
||||
for member in relation.members:
|
||||
if member.type_ == "way":
|
||||
if member.role == "inner":
|
||||
|
@ -394,8 +394,8 @@ class Constructor:
|
|||
else:
|
||||
logging.warning(f'Unknown member role "{member.role}".')
|
||||
if outer_ways:
|
||||
inners_path: list[list[OSMNode]] = glue(inner_ways)
|
||||
outers_path: list[list[OSMNode]] = glue(outer_ways)
|
||||
inners_path: List[List[OSMNode]] = glue(inner_ways)
|
||||
outers_path: List[List[OSMNode]] = glue(outer_ways)
|
||||
self.construct_line(relation, inners_path, outers_path)
|
||||
|
||||
def construct_nodes(self) -> None:
|
||||
|
@ -414,11 +414,11 @@ class Constructor:
|
|||
|
||||
def construct_node(self, node: OSMNode) -> None:
|
||||
"""Draw one node."""
|
||||
tags: dict[str, str] = node.tags
|
||||
tags: Dict[str, str] = node.tags
|
||||
if not self.check_level(tags):
|
||||
return
|
||||
|
||||
processed: set[str] = set()
|
||||
processed: Set[str] = set()
|
||||
|
||||
flung: np.ndarray = self.flinger.fling(node.coordinates)
|
||||
|
||||
|
@ -455,7 +455,7 @@ class Constructor:
|
|||
)
|
||||
if icon_set is None:
|
||||
return
|
||||
labels: list[Label] = self.scheme.construct_text(tags, "all", processed)
|
||||
labels: List[Label] = self.scheme.construct_text(tags, "all", processed)
|
||||
self.scheme.process_ignored(tags, processed)
|
||||
|
||||
if node.get_tag("natural") == "tree" and (
|
||||
|
@ -483,7 +483,7 @@ class Constructor:
|
|||
self.points.append(point)
|
||||
|
||||
|
||||
def check_level_number(tags: dict[str, Any], level: float) -> bool:
|
||||
def check_level_number(tags: Dict[str, Any], level: float) -> bool:
|
||||
"""Check if element described by tags is no the specified level."""
|
||||
if "level" in tags:
|
||||
if level not in parse_levels(tags["level"]):
|
||||
|
@ -493,7 +493,7 @@ def check_level_number(tags: dict[str, Any], level: float) -> bool:
|
|||
return True
|
||||
|
||||
|
||||
def check_level_overground(tags: dict[str, Any]) -> bool:
|
||||
def check_level_overground(tags: Dict[str, Any]) -> bool:
|
||||
"""Check if element described by tags is overground."""
|
||||
if "level" in tags:
|
||||
try:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
"""
|
||||
Direction tag support.
|
||||
"""
|
||||
from typing import Iterator, Optional
|
||||
from typing import Iterator, List, Optional
|
||||
|
||||
import numpy as np
|
||||
from portolan import middle
|
||||
|
@ -65,7 +65,7 @@ class Sector:
|
|||
self.main_direction: Optional[np.ndarray] = None
|
||||
|
||||
if "-" in text:
|
||||
parts: list[str] = text.split("-")
|
||||
parts: List[str] = text.split("-")
|
||||
self.start = parse_vector(parts[0])
|
||||
self.end = parse_vector(parts[1])
|
||||
self.main_direction = (self.start + self.end) / 2
|
||||
|
@ -152,7 +152,7 @@ class DirectionSet:
|
|||
:return: true if direction is right, false if direction is left, and
|
||||
None otherwise.
|
||||
"""
|
||||
result: list[bool] = [x.is_right() for x in self.sectors]
|
||||
result: List[bool] = [x.is_right() for x in self.sectors]
|
||||
if result == [True] * len(result):
|
||||
return True
|
||||
if result == [False] * len(result):
|
||||
|
|
|
@ -3,7 +3,7 @@ Drawing utility.
|
|||
"""
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Optional, Union
|
||||
from typing import List, Optional, Union
|
||||
|
||||
import cairo
|
||||
import numpy as np
|
||||
|
@ -18,7 +18,7 @@ from svgwrite.text import Text
|
|||
__author__ = "Sergey Vartanov"
|
||||
__email__ = "me@enzet.ru"
|
||||
|
||||
PathCommands = list[Union[float, str, np.ndarray]]
|
||||
PathCommands = List[Union[float, str, np.ndarray]]
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -75,7 +75,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
|
||||
|
||||
|
@ -117,7 +117,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:
|
||||
|
@ -169,7 +169,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]))
|
||||
|
@ -283,7 +283,7 @@ class PNGDrawing(Drawing):
|
|||
|
||||
def parse_path(path: str) -> PathCommands:
|
||||
"""Parse path command from text representation into list."""
|
||||
parts: list[str] = path.split(" ")
|
||||
parts: List[str] = path.split(" ")
|
||||
result: PathCommands = []
|
||||
command: str = "M"
|
||||
index: int = 0
|
||||
|
@ -296,7 +296,7 @@ def parse_path(path: str) -> PathCommands:
|
|||
result.append(float(part))
|
||||
else:
|
||||
if "," in part:
|
||||
elements: list[str] = part.split(",")
|
||||
elements: List[str] = part.split(",")
|
||||
result.append(np.array(list(map(float, elements))))
|
||||
else:
|
||||
result.append(np.array((float(part), float(parts[index + 1]))))
|
||||
|
|
|
@ -4,6 +4,7 @@ Drawing separate map elements.
|
|||
import argparse
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Set
|
||||
|
||||
import numpy as np
|
||||
import svgwrite
|
||||
|
@ -33,17 +34,17 @@ def draw_element(options: argparse.Namespace) -> None:
|
|||
target = "area"
|
||||
tags_description = options.area
|
||||
|
||||
tags: dict[str, str] = dict(
|
||||
tags: Dict[str, str] = dict(
|
||||
[x.split("=") for x in tags_description.split(",")]
|
||||
)
|
||||
scheme: Scheme = Scheme(workspace.DEFAULT_SCHEME_PATH)
|
||||
extractor: ShapeExtractor = ShapeExtractor(
|
||||
workspace.ICONS_PATH, workspace.ICONS_CONFIG_PATH
|
||||
)
|
||||
processed: set[str] = set()
|
||||
processed: Set[str] = set()
|
||||
icon, priority = scheme.get_icon(extractor, tags, processed)
|
||||
is_for_node: bool = target == "node"
|
||||
labels: list[Label] = scheme.construct_text(tags, "all", processed)
|
||||
labels: List[Label] = scheme.construct_text(tags, "all", processed)
|
||||
point: Point = Point(
|
||||
icon,
|
||||
labels,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
"""
|
||||
Figures displayed on the map.
|
||||
"""
|
||||
from typing import Any, Iterator, Optional
|
||||
from typing import Any, Dict, Iterator, List, Optional
|
||||
|
||||
import numpy as np
|
||||
from colour import Color
|
||||
|
@ -31,14 +31,14 @@ 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__(tags)
|
||||
|
||||
self.inners: list[list[OSMNode]] = list(map(make_clockwise, inners))
|
||||
self.outers: list[list[OSMNode]] = list(
|
||||
self.inners: List[List[OSMNode]] = list(map(make_clockwise, inners))
|
||||
self.outers: List[List[OSMNode]] = list(
|
||||
map(make_counter_clockwise, outers)
|
||||
)
|
||||
|
||||
|
@ -69,20 +69,20 @@ 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,
|
||||
}
|
||||
self.line_style: LineStyle = LineStyle(style)
|
||||
self.parts: list[Segment] = []
|
||||
self.parts: List[Segment] = []
|
||||
|
||||
for nodes in self.inners + self.outers:
|
||||
for i in range(len(nodes) - 1):
|
||||
|
@ -198,9 +198,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)
|
||||
|
@ -213,7 +213,7 @@ class Crater(Tagged):
|
|||
"""
|
||||
|
||||
def __init__(
|
||||
self, tags: dict[str, str], coordinates: np.ndarray, point: np.ndarray
|
||||
self, tags: Dict[str, str], coordinates: np.ndarray, point: np.ndarray
|
||||
) -> None:
|
||||
super().__init__(tags)
|
||||
self.coordinates: np.ndarray = coordinates
|
||||
|
@ -252,7 +252,7 @@ class Tree(Tagged):
|
|||
"""
|
||||
|
||||
def __init__(
|
||||
self, tags: dict[str, str], coordinates: np.ndarray, point: np.ndarray
|
||||
self, tags: Dict[str, str], coordinates: np.ndarray, point: np.ndarray
|
||||
) -> None:
|
||||
super().__init__(tags)
|
||||
self.coordinates: np.ndarray = coordinates
|
||||
|
@ -279,7 +279,7 @@ class DirectionSector(Tagged):
|
|||
Sector that represents direction.
|
||||
"""
|
||||
|
||||
def __init__(self, tags: dict[str, str], point: np.ndarray) -> None:
|
||||
def __init__(self, tags: Dict[str, str], point: np.ndarray) -> None:
|
||||
super().__init__(tags)
|
||||
self.point: np.ndarray = point
|
||||
|
||||
|
@ -366,7 +366,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.
|
||||
|
||||
|
@ -381,7 +381,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.
|
||||
|
||||
|
@ -390,7 +390,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.
|
||||
|
||||
|
@ -399,7 +399,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.ndarray, flinger: Flinger) -> str:
|
||||
def get_path(nodes: List[OSMNode], shift: np.ndarray, flinger: Flinger) -> str:
|
||||
"""Construct SVG path commands from nodes."""
|
||||
return Polyline(
|
||||
[flinger.fling(x.coordinates) + shift for x in nodes]
|
||||
|
|
|
@ -4,7 +4,7 @@ Icon grid drawing.
|
|||
import logging
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Optional, Union
|
||||
from typing import Dict, List, Optional, Set, Union
|
||||
|
||||
import numpy as np
|
||||
from colour import Color
|
||||
|
@ -25,7 +25,7 @@ class IconCollection:
|
|||
Collection of icons.
|
||||
"""
|
||||
|
||||
icons: list[Icon]
|
||||
icons: List[Icon]
|
||||
|
||||
@classmethod
|
||||
def from_scheme(
|
||||
|
@ -48,11 +48,11 @@ class IconCollection:
|
|||
tags
|
||||
:param add_all: create icons from all possible shapes including parts
|
||||
"""
|
||||
icons: list[Icon] = []
|
||||
icons: List[Icon] = []
|
||||
|
||||
def add() -> Icon:
|
||||
"""Construct icon and add it to the list."""
|
||||
specifications: list[ShapeSpecification] = [
|
||||
specifications: List[ShapeSpecification] = [
|
||||
scheme.get_shape_specification(x, extractor)
|
||||
for x in current_set
|
||||
]
|
||||
|
@ -63,7 +63,7 @@ class IconCollection:
|
|||
|
||||
return constructed_icon
|
||||
|
||||
current_set: list[Union[str, dict[str, str]]]
|
||||
current_set: List[Union[str, Dict[str, str]]]
|
||||
|
||||
for matcher in scheme.node_matchers:
|
||||
matcher: NodeMatcher
|
||||
|
@ -83,7 +83,7 @@ class IconCollection:
|
|||
continue
|
||||
for icon_id in matcher.under_icon:
|
||||
for icon_2_id in matcher.with_icon:
|
||||
current_set: list[str] = (
|
||||
current_set: List[str] = (
|
||||
[icon_id] + [icon_2_id] + matcher.over_icon
|
||||
)
|
||||
add()
|
||||
|
@ -102,7 +102,7 @@ class IconCollection:
|
|||
):
|
||||
add()
|
||||
|
||||
specified_ids: set[str] = set()
|
||||
specified_ids: Set[str] = set()
|
||||
|
||||
for icon in icons:
|
||||
specified_ids |= set(icon.get_shape_ids())
|
||||
|
|
|
@ -6,7 +6,7 @@ import logging
|
|||
import re
|
||||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
from typing import Any, Optional
|
||||
from typing import Any, Dict, List, Optional, Set
|
||||
from xml.etree import ElementTree
|
||||
from xml.etree.ElementTree import Element
|
||||
|
||||
|
@ -47,13 +47,13 @@ class Shape:
|
|||
id_: str # shape identifier
|
||||
name: Optional[str] = None # icon description
|
||||
is_right_directed: Optional[bool] = None
|
||||
emojis: set[str] = field(default_factory=set)
|
||||
emojis: Set[str] = field(default_factory=set)
|
||||
is_part: bool = False
|
||||
|
||||
@classmethod
|
||||
def from_structure(
|
||||
cls,
|
||||
structure: dict[str, Any],
|
||||
structure: Dict[str, Any],
|
||||
path: str,
|
||||
offset: np.ndarray,
|
||||
id_: str,
|
||||
|
@ -105,7 +105,7 @@ class Shape:
|
|||
:param offset: additional offset
|
||||
:param scale: scale resulting image
|
||||
"""
|
||||
transformations: list[str] = []
|
||||
transformations: List[str] = []
|
||||
shift: np.ndarray = point + offset
|
||||
|
||||
transformations.append(f"translate({shift[0]},{shift[1]})")
|
||||
|
@ -138,7 +138,7 @@ def verify_sketch_element(element: Element, id_: str) -> bool:
|
|||
if "style" not in element.attrib or not element.attrib["style"]:
|
||||
return True
|
||||
|
||||
style: dict[str, str] = dict(
|
||||
style: Dict[str, str] = dict(
|
||||
[x.split(":") for x in element.attrib["style"].split(";")]
|
||||
)
|
||||
if (
|
||||
|
@ -180,8 +180,8 @@ class ShapeExtractor:
|
|||
:param svg_file_name: input SVG file name with icons. File may contain
|
||||
any other irrelevant graphics.
|
||||
"""
|
||||
self.shapes: dict[str, Shape] = {}
|
||||
self.configuration: dict[str, Any] = json.load(
|
||||
self.shapes: Dict[str, Shape] = {}
|
||||
self.configuration: Dict[str, Any] = json.load(
|
||||
configuration_file_name.open()
|
||||
)
|
||||
root: Element = ElementTree.parse(svg_file_name).getroot()
|
||||
|
@ -229,7 +229,7 @@ class ShapeExtractor:
|
|||
name = child_node.text
|
||||
break
|
||||
|
||||
configuration: dict[str, Any] = (
|
||||
configuration: Dict[str, Any] = (
|
||||
self.configuration[id_] if id_ in self.configuration else {}
|
||||
)
|
||||
self.shapes[id_] = Shape.from_structure(
|
||||
|
@ -271,7 +271,7 @@ class ShapeSpecification:
|
|||
self,
|
||||
svg: BaseElement,
|
||||
point: np.ndarray,
|
||||
tags: dict[str, Any] = None,
|
||||
tags: Dict[str, Any] = None,
|
||||
outline: bool = False,
|
||||
outline_opacity: float = 1.0,
|
||||
) -> None:
|
||||
|
@ -299,7 +299,7 @@ class ShapeSpecification:
|
|||
bright: bool = is_bright(self.color)
|
||||
color: Color = Color("black") if bright else Color("white")
|
||||
|
||||
style: dict[str, Any] = {
|
||||
style: Dict[str, Any] = {
|
||||
"fill": color.hex,
|
||||
"stroke": color.hex,
|
||||
"stroke-width": 2.2,
|
||||
|
@ -330,14 +330,14 @@ class Icon:
|
|||
Icon that consists of (probably) multiple shapes.
|
||||
"""
|
||||
|
||||
shape_specifications: list[ShapeSpecification]
|
||||
shape_specifications: List[ShapeSpecification]
|
||||
opacity: float = 1.0
|
||||
|
||||
def get_shape_ids(self) -> list[str]:
|
||||
def get_shape_ids(self) -> List[str]:
|
||||
"""Get all shape identifiers in the icon."""
|
||||
return [x.shape.id_ for x in self.shape_specifications]
|
||||
|
||||
def get_names(self) -> list[str]:
|
||||
def get_names(self) -> List[str]:
|
||||
"""Get all shape names in the icon."""
|
||||
return [
|
||||
(x.shape.name if x.shape.name else "unknown")
|
||||
|
@ -348,7 +348,7 @@ class Icon:
|
|||
self,
|
||||
svg: svgwrite.Drawing,
|
||||
point: np.ndarray,
|
||||
tags: dict[str, Any] = None,
|
||||
tags: Dict[str, Any] = None,
|
||||
outline: bool = False,
|
||||
) -> None:
|
||||
"""
|
||||
|
@ -420,7 +420,7 @@ class Icon:
|
|||
shape_specification.color = color
|
||||
|
||||
def add_specifications(
|
||||
self, specifications: list[ShapeSpecification]
|
||||
self, specifications: List[ShapeSpecification]
|
||||
) -> None:
|
||||
"""Add shape specifications to the icon."""
|
||||
self.shape_specifications += specifications
|
||||
|
@ -443,8 +443,8 @@ class IconSet:
|
|||
"""
|
||||
|
||||
main_icon: Icon
|
||||
extra_icons: list[Icon]
|
||||
extra_icons: List[Icon]
|
||||
|
||||
# Tag keys that were processed to create icon set (other tag keys should be
|
||||
# displayed by text or ignored)
|
||||
processed: set[str]
|
||||
processed: Set[str]
|
||||
|
|
|
@ -4,7 +4,7 @@ MapCSS scheme creation.
|
|||
import argparse
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Optional, TextIO
|
||||
from typing import Dict, List, Optional, TextIO
|
||||
|
||||
from colour import Color
|
||||
|
||||
|
@ -84,8 +84,8 @@ class MapCSSWriter:
|
|||
self.add_icons_for_lifecycle: bool = add_icons_for_lifecycle
|
||||
self.icon_directory_name: str = icon_directory_name
|
||||
|
||||
self.point_matchers: list[Matcher] = scheme.node_matchers
|
||||
self.line_matchers: list[Matcher] = scheme.way_matchers
|
||||
self.point_matchers: List[Matcher] = scheme.node_matchers
|
||||
self.line_matchers: List[Matcher] = scheme.way_matchers
|
||||
|
||||
def add_selector(
|
||||
self,
|
||||
|
@ -103,7 +103,7 @@ class MapCSSWriter:
|
|||
:param opacity: icon opacity
|
||||
:return: string representation of selector
|
||||
"""
|
||||
elements: dict[str, str] = {}
|
||||
elements: Dict[str, str] = {}
|
||||
|
||||
clean_shapes = matcher.get_clean_shapes()
|
||||
if clean_shapes:
|
||||
|
@ -116,7 +116,7 @@ class MapCSSWriter:
|
|||
if opacity is not None:
|
||||
elements["icon-opacity"] = f"{opacity:.2f}"
|
||||
|
||||
style: dict[str, str] = matcher.get_style()
|
||||
style: Dict[str, str] = matcher.get_style()
|
||||
if style:
|
||||
if "fill" in style:
|
||||
elements["fill-color"] = style["fill"]
|
||||
|
|
|
@ -4,7 +4,7 @@ Simple OpenStreetMap renderer.
|
|||
import argparse
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Iterator, Optional
|
||||
from typing import Dict, Iterator, List, Optional, Set
|
||||
|
||||
import numpy as np
|
||||
import svgwrite
|
||||
|
@ -57,7 +57,7 @@ class Map:
|
|||
self.svg.add(
|
||||
Rect((0, 0), self.flinger.size, fill=self.background_color)
|
||||
)
|
||||
ways: list[StyledFigure] = sorted(
|
||||
ways: List[StyledFigure] = sorted(
|
||||
constructor.figures, key=lambda x: x.line_style.priority
|
||||
)
|
||||
ways_length: int = len(ways)
|
||||
|
@ -94,7 +94,7 @@ class Map:
|
|||
self.configuration.overlap,
|
||||
)
|
||||
|
||||
nodes: list[Point] = sorted(
|
||||
nodes: List[Point] = sorted(
|
||||
constructor.points, key=lambda x: -x.priority
|
||||
)
|
||||
steps: int = len(nodes)
|
||||
|
@ -156,7 +156,7 @@ class Map:
|
|||
|
||||
def draw_roads(self, roads: Iterator[Road]) -> None:
|
||||
"""Draw road as simple SVG path."""
|
||||
nodes: dict[OSMNode, set[RoadPart]] = {}
|
||||
nodes: Dict[OSMNode, Set[RoadPart]] = {}
|
||||
|
||||
for road in roads:
|
||||
for index in range(len(road.outers[0]) - 1):
|
||||
|
@ -177,7 +177,7 @@ class Map:
|
|||
nodes[node_2].add(part_2)
|
||||
|
||||
for node in nodes:
|
||||
parts: set[RoadPart] = nodes[node]
|
||||
parts: Set[RoadPart] = nodes[node]
|
||||
if len(parts) < 4:
|
||||
continue
|
||||
intersection: Intersection = Intersection(list(parts))
|
||||
|
@ -197,7 +197,7 @@ def ui(arguments: argparse.Namespace) -> None:
|
|||
cache_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
boundary_box: Optional[BoundaryBox] = None
|
||||
input_file_names: list[Path] = []
|
||||
input_file_names: List[Path] = []
|
||||
|
||||
if arguments.input_file_names:
|
||||
input_file_names = list(map(Path, arguments.input_file_names))
|
||||
|
|
|
@ -4,7 +4,7 @@ Moire markup extension for Map Machine.
|
|||
import argparse
|
||||
from abc import ABC
|
||||
from pathlib import Path
|
||||
from typing import Any, Union
|
||||
from typing import Any, Dict, List, Union
|
||||
|
||||
from moire.default import Default, DefaultHTML, DefaultMarkdown, DefaultWiki
|
||||
from moire.moire import Tag
|
||||
|
@ -17,7 +17,7 @@ from map_machine.workspace import workspace
|
|||
__author__ = "Sergey Vartanov"
|
||||
__email__ = "me@enzet.ru"
|
||||
|
||||
Arguments = list[Any]
|
||||
Arguments = List[Any]
|
||||
Code = Union[str, Tag, list]
|
||||
|
||||
PREFIX: str = "https://wiki.openstreetmap.org/wiki/"
|
||||
|
@ -50,13 +50,13 @@ class ArgumentParser(argparse.ArgumentParser):
|
|||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs) -> None:
|
||||
self.arguments: list[dict[str, Any]] = []
|
||||
self.arguments: List[Dict[str, Any]] = []
|
||||
super(ArgumentParser, self).__init__(*args, **kwargs)
|
||||
|
||||
def add_argument(self, *args, **kwargs) -> None:
|
||||
"""Just store argument with options."""
|
||||
super(ArgumentParser, self).add_argument(*args, **kwargs)
|
||||
argument: dict[str, Any] = {"arguments": [x for x in args]}
|
||||
argument: Dict[str, Any] = {"arguments": [x for x in args]}
|
||||
|
||||
for key in kwargs:
|
||||
argument[key] = kwargs[key]
|
||||
|
|
|
@ -5,6 +5,7 @@ import logging
|
|||
import time
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Dict
|
||||
|
||||
import urllib3
|
||||
|
||||
|
@ -48,7 +49,7 @@ def get_osm(
|
|||
return content.decode("utf-8")
|
||||
|
||||
|
||||
def get_data(address: str, parameters: dict[str, str]) -> bytes:
|
||||
def get_data(address: str, parameters: Dict[str, str]) -> bytes:
|
||||
"""
|
||||
Construct Internet page URL and get its descriptor.
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import re
|
|||
from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Any, Optional
|
||||
from typing import Any, Dict, List, Optional, Set
|
||||
from xml.etree import ElementTree
|
||||
from xml.etree.ElementTree import Element
|
||||
|
||||
|
@ -27,7 +27,7 @@ MILES_PATTERN: re.Pattern = re.compile("^(?P<value>\\d*\\.?\\d*)\\s*mi$")
|
|||
|
||||
|
||||
# See https://wiki.openstreetmap.org/wiki/Lifecycle_prefix#Stages_of_decay
|
||||
STAGES_OF_DECAY: list[str] = [
|
||||
STAGES_OF_DECAY: List[str] = [
|
||||
"disused",
|
||||
"abandoned",
|
||||
"ruins",
|
||||
|
@ -47,7 +47,7 @@ def parse_float(string: str) -> Optional[float]:
|
|||
return None
|
||||
|
||||
|
||||
def parse_levels(string: str) -> list[float]:
|
||||
def parse_levels(string: str) -> List[float]:
|
||||
"""Parse string representation of level sequence value."""
|
||||
# TODO: add `-` parsing
|
||||
try:
|
||||
|
@ -63,7 +63,7 @@ class Tagged:
|
|||
Something with tags (string to string mapping).
|
||||
"""
|
||||
|
||||
tags: dict[str, str]
|
||||
tags: Dict[str, str]
|
||||
|
||||
def get_tag(self, key: str) -> Optional[str]:
|
||||
"""
|
||||
|
@ -127,7 +127,7 @@ class OSMNode(Tagged):
|
|||
def from_xml_structure(cls, element: Element) -> "OSMNode":
|
||||
"""Parse node from OSM XML `<node>` element."""
|
||||
attributes = element.attrib
|
||||
tags: dict[str, str] = dict(
|
||||
tags: Dict[str, str] = dict(
|
||||
[(x.attrib["k"], x.attrib["v"]) for x in element if x.tag == "tag"]
|
||||
)
|
||||
return cls(
|
||||
|
@ -144,7 +144,7 @@ class OSMNode(Tagged):
|
|||
)
|
||||
|
||||
@classmethod
|
||||
def parse_from_structure(cls, structure: dict[str, Any]) -> "OSMNode":
|
||||
def parse_from_structure(cls, structure: Dict[str, Any]) -> "OSMNode":
|
||||
"""
|
||||
Parse node from Overpass-like structure.
|
||||
|
||||
|
@ -169,7 +169,7 @@ class OSMWay(Tagged):
|
|||
"""
|
||||
|
||||
id_: int
|
||||
nodes: Optional[list[OSMNode]] = field(default_factory=list)
|
||||
nodes: Optional[List[OSMNode]] = field(default_factory=list)
|
||||
visible: Optional[str] = None
|
||||
changeset: Optional[str] = None
|
||||
timestamp: Optional[datetime] = None
|
||||
|
@ -178,11 +178,11 @@ class OSMWay(Tagged):
|
|||
|
||||
@classmethod
|
||||
def from_xml_structure(
|
||||
cls, element: Element, nodes: dict[int, OSMNode]
|
||||
cls, element: Element, nodes: Dict[int, OSMNode]
|
||||
) -> "OSMWay":
|
||||
"""Parse way from OSM XML `<way>` element."""
|
||||
attributes = element.attrib
|
||||
tags: dict[str, str] = dict(
|
||||
tags: Dict[str, str] = dict(
|
||||
[(x.attrib["k"], x.attrib["v"]) for x in element if x.tag == "tag"]
|
||||
)
|
||||
return cls(
|
||||
|
@ -200,7 +200,7 @@ class OSMWay(Tagged):
|
|||
|
||||
@classmethod
|
||||
def parse_from_structure(
|
||||
cls, structure: dict[str, Any], nodes: dict[int, OSMNode]
|
||||
cls, structure: Dict[str, Any], nodes: Dict[int, OSMNode]
|
||||
) -> "OSMWay":
|
||||
"""
|
||||
Parse way from Overpass-like structure.
|
||||
|
@ -242,7 +242,7 @@ class OSMRelation(Tagged):
|
|||
"""
|
||||
|
||||
id_: int
|
||||
members: Optional[list[OSMMember]]
|
||||
members: Optional[List[OSMMember]]
|
||||
visible: Optional[str] = None
|
||||
changeset: Optional[str] = None
|
||||
timestamp: Optional[datetime] = None
|
||||
|
@ -253,8 +253,8 @@ class OSMRelation(Tagged):
|
|||
def from_xml_structure(cls, element: Element) -> "OSMRelation":
|
||||
"""Parse relation from OSM XML `<relation>` element."""
|
||||
attributes = element.attrib
|
||||
members: list[OSMMember] = []
|
||||
tags: dict[str, str] = {}
|
||||
members: List[OSMMember] = []
|
||||
tags: Dict[str, str] = {}
|
||||
for subelement in element:
|
||||
if subelement.tag == "member":
|
||||
subattributes = subelement.attrib
|
||||
|
@ -281,7 +281,7 @@ class OSMRelation(Tagged):
|
|||
)
|
||||
|
||||
@classmethod
|
||||
def parse_from_structure(cls, structure: dict[str, Any]) -> "OSMRelation":
|
||||
def parse_from_structure(cls, structure: Dict[str, Any]) -> "OSMRelation":
|
||||
"""
|
||||
Parse relation from Overpass-like structure.
|
||||
|
||||
|
@ -311,12 +311,12 @@ class OSMData:
|
|||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.nodes: dict[int, OSMNode] = {}
|
||||
self.ways: dict[int, OSMWay] = {}
|
||||
self.relations: dict[int, OSMRelation] = {}
|
||||
self.nodes: Dict[int, OSMNode] = {}
|
||||
self.ways: Dict[int, OSMWay] = {}
|
||||
self.relations: Dict[int, OSMRelation] = {}
|
||||
|
||||
self.authors: set[str] = set()
|
||||
self.levels: set[float] = set()
|
||||
self.authors: Set[str] = set()
|
||||
self.levels: Set[float] = set()
|
||||
self.time: MinMax = MinMax()
|
||||
self.view_box: Optional[BoundaryBox] = None
|
||||
self.equator_length: float = 40_075_017.0
|
||||
|
@ -364,8 +364,8 @@ class OSMData:
|
|||
with file_name.open() as input_file:
|
||||
structure = json.load(input_file)
|
||||
|
||||
node_map: dict[int, OSMNode] = {}
|
||||
way_map: dict[int, OSMWay] = {}
|
||||
node_map: Dict[int, OSMNode] = {}
|
||||
way_map: Dict[int, OSMWay] = {}
|
||||
|
||||
for element in structure["elements"]:
|
||||
if element["type"] == "node":
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
"""
|
||||
Point: node representation on the map.
|
||||
"""
|
||||
from typing import Optional
|
||||
from typing import Dict, List, Optional, Set
|
||||
|
||||
import numpy as np
|
||||
import svgwrite
|
||||
|
@ -53,9 +53,9 @@ class Point(Tagged):
|
|||
def __init__(
|
||||
self,
|
||||
icon_set: IconSet,
|
||||
labels: list[Label],
|
||||
tags: dict[str, str],
|
||||
processed: set[str],
|
||||
labels: List[Label],
|
||||
tags: Dict[str, str],
|
||||
processed: Set[str],
|
||||
point: np.ndarray,
|
||||
priority: float = 0,
|
||||
is_for_node: bool = True,
|
||||
|
@ -67,8 +67,8 @@ class Point(Tagged):
|
|||
assert point is not None
|
||||
|
||||
self.icon_set: IconSet = icon_set
|
||||
self.labels: list[Label] = labels
|
||||
self.processed: set[str] = processed
|
||||
self.labels: List[Label] = labels
|
||||
self.processed: Set[str] = processed
|
||||
self.point: np.ndarray = point
|
||||
self.priority: float = priority
|
||||
self.layer: float = 0
|
||||
|
@ -92,7 +92,7 @@ class Point(Tagged):
|
|||
return
|
||||
|
||||
position: np.ndarray = self.point + np.array((0, self.y))
|
||||
tags: Optional[dict[str, str]] = (
|
||||
tags: Optional[Dict[str, str]] = (
|
||||
self.tags if self.add_tooltips else None
|
||||
)
|
||||
self.main_icon_painted: bool = self.draw_point_shape(
|
||||
|
@ -135,7 +135,7 @@ class Point(Tagged):
|
|||
icon: Icon,
|
||||
position: np.ndarray,
|
||||
occupied: Occupied,
|
||||
tags: Optional[dict[str, str]] = None,
|
||||
tags: Optional[Dict[str, str]] = None,
|
||||
) -> bool:
|
||||
"""Draw one combined icon and its outline."""
|
||||
# Down-cast floats to integers to make icons pixel-perfect.
|
||||
|
@ -166,7 +166,7 @@ class Point(Tagged):
|
|||
label_mode: str = LabelMode.MAIN,
|
||||
) -> None:
|
||||
"""Draw all labels."""
|
||||
labels: list[Label]
|
||||
labels: List[Label]
|
||||
|
||||
if label_mode == LabelMode.MAIN:
|
||||
labels = self.labels[:1]
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
WIP: road shape drawing.
|
||||
"""
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Optional
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
import numpy as np
|
||||
import svgwrite
|
||||
|
@ -62,7 +62,7 @@ class RoadPart:
|
|||
self,
|
||||
point_1: np.ndarray,
|
||||
point_2: np.ndarray,
|
||||
lanes: list[Lane],
|
||||
lanes: List[Lane],
|
||||
scale: float,
|
||||
) -> None:
|
||||
"""
|
||||
|
@ -72,7 +72,7 @@ class RoadPart:
|
|||
"""
|
||||
self.point_1: np.ndarray = point_1
|
||||
self.point_2: np.ndarray = point_2
|
||||
self.lanes: list[Lane] = lanes
|
||||
self.lanes: List[Lane] = lanes
|
||||
if lanes:
|
||||
self.width = sum(map(lambda x: x.get_width(scale), lanes))
|
||||
else:
|
||||
|
@ -284,8 +284,8 @@ class Intersection:
|
|||
points of the road parts should be the same.
|
||||
"""
|
||||
|
||||
def __init__(self, parts: list[RoadPart]) -> None:
|
||||
self.parts: list[RoadPart] = sorted(parts, key=lambda x: x.get_angle())
|
||||
def __init__(self, parts: List[RoadPart]) -> None:
|
||||
self.parts: List[RoadPart] = sorted(parts, key=lambda x: x.get_angle())
|
||||
|
||||
for index in range(len(self.parts)):
|
||||
next_index: int = 0 if index == len(self.parts) - 1 else index + 1
|
||||
|
@ -368,20 +368,20 @@ class Road(Tagged):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
tags: dict[str, str],
|
||||
nodes: list[OSMNode],
|
||||
tags: Dict[str, str],
|
||||
nodes: List[OSMNode],
|
||||
matcher: RoadMatcher,
|
||||
flinger: Flinger,
|
||||
) -> None:
|
||||
super().__init__(tags)
|
||||
self.nodes: list[OSMNode] = nodes
|
||||
self.nodes: List[OSMNode] = nodes
|
||||
self.matcher: RoadMatcher = matcher
|
||||
|
||||
self.line: Polyline = Polyline(
|
||||
[flinger.fling(x.coordinates) for x in self.nodes]
|
||||
)
|
||||
self.width: Optional[float] = matcher.default_width
|
||||
self.lanes: list[Lane] = []
|
||||
self.lanes: List[Lane] = []
|
||||
|
||||
if "lanes" in tags:
|
||||
try:
|
||||
|
@ -391,7 +391,7 @@ class Road(Tagged):
|
|||
pass
|
||||
|
||||
if "width:lanes" in tags:
|
||||
widths: list[float] = list(
|
||||
widths: List[float] = list(
|
||||
map(float, tags["width:lanes"].split("|"))
|
||||
)
|
||||
if len(widths) == len(self.lanes):
|
||||
|
@ -434,7 +434,7 @@ class Road(Tagged):
|
|||
scale: float = flinger.get_scale(self.nodes[0].coordinates)
|
||||
path_commands: str = self.line.get_path()
|
||||
path: Path = Path(d=path_commands)
|
||||
style: dict[str, Any] = {
|
||||
style: Dict[str, Any] = {
|
||||
"fill": "none",
|
||||
"stroke": color.hex,
|
||||
"stroke-linecap": "butt",
|
||||
|
@ -454,7 +454,7 @@ class Road(Tagged):
|
|||
-self.width / 2 + index * self.width / len(self.lanes)
|
||||
)
|
||||
path: Path = Path(d=self.line.get_path(parallel_offset))
|
||||
style: dict[str, Any] = {
|
||||
style: Dict[str, Any] = {
|
||||
"fill": "none",
|
||||
"stroke": color.hex,
|
||||
"stroke-linejoin": "round",
|
||||
|
@ -467,7 +467,7 @@ class Road(Tagged):
|
|||
|
||||
def get_curve_points(
|
||||
road: Road, scale: float, center: np.ndarray, road_end: np.ndarray
|
||||
) -> list[np.ndarray]:
|
||||
) -> List[np.ndarray]:
|
||||
"""
|
||||
:param road: road segment
|
||||
:param scale: current zoom scale
|
||||
|
@ -492,11 +492,11 @@ class Connector:
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
connections: list[tuple[Road, int]],
|
||||
connections: List[Tuple[Road, int]],
|
||||
flinger: Flinger,
|
||||
scale: float,
|
||||
) -> None:
|
||||
self.connections: list[tuple[Road, int]] = connections
|
||||
self.connections: List[Tuple[Road, int]] = connections
|
||||
self.road_1: Road = connections[0][0]
|
||||
self.index_1: int = connections[0][1]
|
||||
|
||||
|
@ -520,7 +520,7 @@ class SimpleConnector(Connector):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
connections: list[tuple[Road, int]],
|
||||
connections: List[Tuple[Road, int]],
|
||||
flinger: Flinger,
|
||||
scale: float,
|
||||
) -> None:
|
||||
|
@ -558,7 +558,7 @@ class ComplexConnector(Connector):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
connections: list[tuple[Road, int]],
|
||||
connections: List[Tuple[Road, int]],
|
||||
flinger: Flinger,
|
||||
scale: float,
|
||||
) -> None:
|
||||
|
@ -574,10 +574,10 @@ class ComplexConnector(Connector):
|
|||
node: OSMNode = self.road_1.nodes[self.index_1]
|
||||
point: np.ndarray = flinger.fling(node.coordinates)
|
||||
|
||||
points_1: list[np.ndarray] = get_curve_points(
|
||||
points_1: List[np.ndarray] = get_curve_points(
|
||||
self.road_1, scale, point, self.road_1.line.points[self.index_1]
|
||||
)
|
||||
points_2: list[np.ndarray] = get_curve_points(
|
||||
points_2: List[np.ndarray] = get_curve_points(
|
||||
self.road_2, scale, point, self.road_2.line.points[self.index_2]
|
||||
)
|
||||
# fmt: off
|
||||
|
@ -626,7 +626,7 @@ class SimpleIntersection(Connector):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
connections: list[tuple[Road, int]],
|
||||
connections: List[Tuple[Road, int]],
|
||||
flinger: Flinger,
|
||||
scale: float,
|
||||
) -> None:
|
||||
|
@ -661,8 +661,8 @@ class Roads:
|
|||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.roads: list[Road] = []
|
||||
self.connections: dict[int, list[tuple[Road, int]]] = {}
|
||||
self.roads: List[Road] = []
|
||||
self.connections: Dict[int, List[Tuple[Road, int]]] = {}
|
||||
|
||||
def append(self, road: Road) -> None:
|
||||
"""Add road and update connections."""
|
||||
|
@ -679,8 +679,8 @@ class Roads:
|
|||
return
|
||||
|
||||
scale: float = flinger.get_scale(self.roads[0].nodes[0].coordinates)
|
||||
layered_roads: dict[float, list[Road]] = {}
|
||||
layered_connectors: dict[float, list[Connector]] = {}
|
||||
layered_roads: Dict[float, List[Road]] = {}
|
||||
layered_connectors: Dict[float, List[Connector]] = {}
|
||||
|
||||
for road in self.roads:
|
||||
if road.layer not in layered_roads:
|
||||
|
@ -688,7 +688,7 @@ class Roads:
|
|||
layered_roads[road.layer].append(road)
|
||||
|
||||
for id_ in self.connections:
|
||||
connected: list[tuple[Road, int]] = self.connections[id_]
|
||||
connected: List[Tuple[Road, int]] = self.connections[id_]
|
||||
connector: Connector
|
||||
|
||||
if len(self.connections[id_]) == 2:
|
||||
|
@ -706,10 +706,10 @@ class Roads:
|
|||
layered_connectors[connector.layer].append(connector)
|
||||
|
||||
for layer in sorted(layered_roads.keys()):
|
||||
roads: list[Road] = sorted(
|
||||
roads: List[Road] = sorted(
|
||||
layered_roads[layer], key=lambda x: x.matcher.priority
|
||||
)
|
||||
connectors: list[Connector]
|
||||
connectors: List[Connector]
|
||||
if layer in layered_connectors:
|
||||
connectors = layered_connectors[layer]
|
||||
else:
|
||||
|
|
|
@ -5,7 +5,7 @@ import logging
|
|||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
from typing import Any, Optional, Union
|
||||
from typing import Any, Dict, List, Optional, Set, Tuple, Union
|
||||
|
||||
import numpy as np
|
||||
import yaml
|
||||
|
@ -27,7 +27,7 @@ from map_machine.text import Label, get_address, get_text
|
|||
__author__ = "Sergey Vartanov"
|
||||
__email__ = "me@enzet.ru"
|
||||
|
||||
IconDescription = list[Union[str, dict[str, str]]]
|
||||
IconDescription = List[Union[str, Dict[str, str]]]
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -36,7 +36,7 @@ class LineStyle:
|
|||
SVG line style and its priority.
|
||||
"""
|
||||
|
||||
style: dict[str, Union[int, float, str]]
|
||||
style: Dict[str, Union[int, float, str]]
|
||||
priority: float = 0.0
|
||||
|
||||
|
||||
|
@ -54,7 +54,7 @@ class MatchingType(Enum):
|
|||
def is_matched_tag(
|
||||
matcher_tag_key: str,
|
||||
matcher_tag_value: Union[str, list],
|
||||
tags: dict[str, str],
|
||||
tags: Dict[str, str],
|
||||
) -> MatchingType:
|
||||
"""
|
||||
Check whether element tags contradict tag matcher.
|
||||
|
@ -90,7 +90,7 @@ def get_selector(key: str, value: str, prefix: str = "") -> str:
|
|||
return f'[{key}="{value}"]'
|
||||
|
||||
|
||||
def match_location(restrictions: dict[str, str], country: str) -> bool:
|
||||
def match_location(restrictions: Dict[str, str], country: str) -> bool:
|
||||
"""Check whether country is matched by location restrictions."""
|
||||
if "exclude" in restrictions and country in restrictions["exclude"]:
|
||||
return False
|
||||
|
@ -109,11 +109,11 @@ class Matcher:
|
|||
"""
|
||||
|
||||
def __init__(
|
||||
self, structure: dict[str, Any], group: Optional[dict[str, Any]] = None
|
||||
self, structure: Dict[str, Any], group: Optional[Dict[str, Any]] = None
|
||||
) -> None:
|
||||
self.tags: dict[str, str] = structure["tags"]
|
||||
self.tags: Dict[str, str] = structure["tags"]
|
||||
|
||||
self.exception: dict[str, str] = {}
|
||||
self.exception: Dict[str, str] = {}
|
||||
if "exception" in structure:
|
||||
self.exception = structure["exception"]
|
||||
|
||||
|
@ -125,7 +125,7 @@ class Matcher:
|
|||
if "replace_shapes" in structure:
|
||||
self.replace_shapes = structure["replace_shapes"]
|
||||
|
||||
self.location_restrictions: dict[str, str] = {}
|
||||
self.location_restrictions: Dict[str, str] = {}
|
||||
if "location_restrictions" in structure:
|
||||
self.location_restrictions = structure["location_restrictions"]
|
||||
|
||||
|
@ -137,7 +137,7 @@ class Matcher:
|
|||
|
||||
def is_matched(
|
||||
self,
|
||||
tags: dict[str, str],
|
||||
tags: Dict[str, str],
|
||||
configuration: Optional[MapConfiguration] = None,
|
||||
) -> bool:
|
||||
"""
|
||||
|
@ -186,11 +186,11 @@ class Matcher:
|
|||
[get_selector(x, y, prefix) for (x, y) in self.tags.items()]
|
||||
)
|
||||
|
||||
def get_clean_shapes(self) -> Optional[list[str]]:
|
||||
def get_clean_shapes(self) -> Optional[List[str]]:
|
||||
"""Get list of shape identifiers for shapes."""
|
||||
return None
|
||||
|
||||
def get_style(self) -> dict[str, Any]:
|
||||
def get_style(self) -> Dict[str, Any]:
|
||||
"""Return way SVG style."""
|
||||
return {}
|
||||
|
||||
|
@ -201,7 +201,7 @@ class NodeMatcher(Matcher):
|
|||
"""
|
||||
|
||||
def __init__(
|
||||
self, structure: dict[str, Any], group: dict[str, Any]
|
||||
self, structure: Dict[str, Any], group: Dict[str, Any]
|
||||
) -> None:
|
||||
# Dictionary with tag keys and values, value lists, or "*"
|
||||
super().__init__(structure, group)
|
||||
|
@ -238,7 +238,7 @@ class NodeMatcher(Matcher):
|
|||
if "with_icon" in structure:
|
||||
self.with_icon = structure["with_icon"]
|
||||
|
||||
def get_clean_shapes(self) -> Optional[list[str]]:
|
||||
def get_clean_shapes(self) -> Optional[List[str]]:
|
||||
"""Get list of shape identifiers for shapes."""
|
||||
if not self.shapes:
|
||||
return None
|
||||
|
@ -250,11 +250,11 @@ class WayMatcher(Matcher):
|
|||
Special tag matcher for ways.
|
||||
"""
|
||||
|
||||
def __init__(self, structure: dict[str, Any], scheme: "Scheme") -> None:
|
||||
def __init__(self, structure: Dict[str, Any], scheme: "Scheme") -> None:
|
||||
super().__init__(structure)
|
||||
self.style: dict[str, Any] = {"fill": "none"}
|
||||
self.style: Dict[str, Any] = {"fill": "none"}
|
||||
if "style" in structure:
|
||||
style: dict[str, Any] = structure["style"]
|
||||
style: Dict[str, Any] = structure["style"]
|
||||
for key in style:
|
||||
if str(style[key]).endswith("_color"):
|
||||
self.style[key] = scheme.get_color(style[key]).hex.upper()
|
||||
|
@ -264,7 +264,7 @@ class WayMatcher(Matcher):
|
|||
if "priority" in structure:
|
||||
self.priority = structure["priority"]
|
||||
|
||||
def get_style(self) -> dict[str, Any]:
|
||||
def get_style(self) -> Dict[str, Any]:
|
||||
"""Return way SVG style."""
|
||||
return self.style
|
||||
|
||||
|
@ -274,7 +274,7 @@ class RoadMatcher(Matcher):
|
|||
Special tag matcher for highways.
|
||||
"""
|
||||
|
||||
def __init__(self, structure: dict[str, Any], scheme: "Scheme") -> None:
|
||||
def __init__(self, structure: Dict[str, Any], scheme: "Scheme") -> None:
|
||||
super().__init__(structure)
|
||||
self.border_color: Color = Color(
|
||||
scheme.get_color(structure["border_color"])
|
||||
|
@ -287,7 +287,7 @@ class RoadMatcher(Matcher):
|
|||
if "priority" in structure:
|
||||
self.priority = structure["priority"]
|
||||
|
||||
def get_priority(self, tags: dict[str, str]) -> float:
|
||||
def get_priority(self, tags: Dict[str, str]) -> float:
|
||||
layer: float = 0
|
||||
if "layer" in tags:
|
||||
layer = float(tags.get("layer"))
|
||||
|
@ -307,33 +307,33 @@ class Scheme:
|
|||
specification
|
||||
"""
|
||||
with file_name.open() as input_file:
|
||||
content: dict[str, Any] = yaml.load(
|
||||
content: Dict[str, Any] = yaml.load(
|
||||
input_file.read(), Loader=yaml.FullLoader
|
||||
)
|
||||
self.node_matchers: list[NodeMatcher] = []
|
||||
self.node_matchers: List[NodeMatcher] = []
|
||||
for group in content["node_icons"]:
|
||||
for element in group["tags"]:
|
||||
self.node_matchers.append(NodeMatcher(element, group))
|
||||
|
||||
self.colors: dict[str, str] = content["colors"]
|
||||
self.material_colors: dict[str, str] = content["material_colors"]
|
||||
self.colors: Dict[str, str] = content["colors"]
|
||||
self.material_colors: Dict[str, str] = content["material_colors"]
|
||||
|
||||
self.way_matchers: list[WayMatcher] = [
|
||||
self.way_matchers: List[WayMatcher] = [
|
||||
WayMatcher(x, self) for x in content["ways"]
|
||||
]
|
||||
self.road_matchers: list[RoadMatcher] = [
|
||||
self.road_matchers: List[RoadMatcher] = [
|
||||
RoadMatcher(x, self) for x in content["roads"]
|
||||
]
|
||||
self.area_matchers: list[Matcher] = [
|
||||
self.area_matchers: List[Matcher] = [
|
||||
Matcher(x) for x in content["area_tags"]
|
||||
]
|
||||
self.tags_to_write: list[str] = content["tags_to_write"]
|
||||
self.prefix_to_write: list[str] = content["prefix_to_write"]
|
||||
self.tags_to_skip: list[str] = content["tags_to_skip"]
|
||||
self.prefix_to_skip: list[str] = content["prefix_to_skip"]
|
||||
self.tags_to_write: List[str] = content["tags_to_write"]
|
||||
self.prefix_to_write: List[str] = content["prefix_to_write"]
|
||||
self.tags_to_skip: List[str] = content["tags_to_skip"]
|
||||
self.prefix_to_skip: List[str] = content["prefix_to_skip"]
|
||||
|
||||
# Storage for created icon sets.
|
||||
self.cache: dict[str, tuple[IconSet, int]] = {}
|
||||
self.cache: Dict[str, Tuple[IconSet, int]] = {}
|
||||
|
||||
def get_color(self, color: str) -> Color:
|
||||
"""
|
||||
|
@ -384,10 +384,10 @@ class Scheme:
|
|||
def get_icon(
|
||||
self,
|
||||
extractor: ShapeExtractor,
|
||||
tags: dict[str, Any],
|
||||
processed: set[str],
|
||||
tags: Dict[str, Any],
|
||||
processed: Set[str],
|
||||
configuration: MapConfiguration = MapConfiguration(),
|
||||
) -> tuple[Optional[IconSet], int]:
|
||||
) -> Tuple[Optional[IconSet], int]:
|
||||
"""
|
||||
Construct icon set.
|
||||
|
||||
|
@ -404,7 +404,7 @@ class Scheme:
|
|||
return self.cache[tags_hash]
|
||||
|
||||
main_icon: Optional[Icon] = None
|
||||
extra_icons: list[Icon] = []
|
||||
extra_icons: List[Icon] = []
|
||||
priority: int = 0
|
||||
|
||||
index: int = 0
|
||||
|
@ -419,7 +419,7 @@ class Scheme:
|
|||
and not matcher.check_zoom_level(configuration.zoom_level)
|
||||
):
|
||||
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
|
||||
if not matcher.draw:
|
||||
processed |= matcher_tags
|
||||
|
@ -492,7 +492,7 @@ class Scheme:
|
|||
|
||||
return returned, priority
|
||||
|
||||
def get_style(self, tags: dict[str, Any]) -> list[LineStyle]:
|
||||
def get_style(self, tags: Dict[str, Any]) -> List[LineStyle]:
|
||||
"""Get line style based on tags and scale."""
|
||||
line_styles = []
|
||||
|
||||
|
@ -504,7 +504,7 @@ class Scheme:
|
|||
|
||||
return line_styles
|
||||
|
||||
def get_road(self, tags: dict[str, Any]) -> Optional[RoadMatcher]:
|
||||
def get_road(self, tags: Dict[str, Any]) -> Optional[RoadMatcher]:
|
||||
"""Get road matcher if tags are matched."""
|
||||
for matcher in self.road_matchers:
|
||||
if not matcher.is_matched(tags):
|
||||
|
@ -513,10 +513,10 @@ class Scheme:
|
|||
return None
|
||||
|
||||
def construct_text(
|
||||
self, tags: dict[str, str], draw_captions: str, processed: set[str]
|
||||
) -> list[Label]:
|
||||
self, tags: Dict[str, str], draw_captions: str, processed: Set[str]
|
||||
) -> List[Label]:
|
||||
"""Construct labels for not processed tags."""
|
||||
texts: list[Label] = []
|
||||
texts: List[Label] = []
|
||||
|
||||
name = None
|
||||
alt_name = None
|
||||
|
@ -542,7 +542,7 @@ class Scheme:
|
|||
alt_name = ""
|
||||
alt_name += "ex " + tags["old_name"]
|
||||
|
||||
address: list[str] = get_address(tags, draw_captions, processed)
|
||||
address: List[str] = get_address(tags, draw_captions, processed)
|
||||
|
||||
if name:
|
||||
texts.append(Label(name, Color("black")))
|
||||
|
@ -587,7 +587,7 @@ class Scheme:
|
|||
texts.append(Label(tags[tag]))
|
||||
return texts
|
||||
|
||||
def is_area(self, tags: dict[str, str]) -> bool:
|
||||
def is_area(self, tags: Dict[str, str]) -> bool:
|
||||
"""Check whether way described by tags is area."""
|
||||
for matcher in self.area_matchers:
|
||||
if matcher.is_matched(tags):
|
||||
|
@ -595,7 +595,7 @@ class Scheme:
|
|||
return False
|
||||
|
||||
def process_ignored(
|
||||
self, tags: dict[str, str], processed: set[str]
|
||||
self, tags: Dict[str, str], processed: Set[str]
|
||||
) -> None:
|
||||
"""
|
||||
Mark all ignored tag as processed.
|
||||
|
@ -607,7 +607,7 @@ class Scheme:
|
|||
|
||||
def get_shape_specification(
|
||||
self,
|
||||
structure: Union[str, dict[str, Any]],
|
||||
structure: Union[str, Dict[str, Any]],
|
||||
extractor: ShapeExtractor,
|
||||
color: Color = DEFAULT_COLOR,
|
||||
) -> ShapeSpecification:
|
||||
|
|
|
@ -5,7 +5,7 @@ import argparse
|
|||
import logging
|
||||
from http.server import HTTPServer, SimpleHTTPRequestHandler
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
import cairosvg
|
||||
|
||||
|
@ -28,14 +28,14 @@ class _Handler(SimpleHTTPRequestHandler):
|
|||
def __init__(
|
||||
self,
|
||||
request: bytes,
|
||||
client_address: tuple[str, int],
|
||||
client_address: Tuple[str, int],
|
||||
server: HTTPServer,
|
||||
) -> None:
|
||||
super().__init__(request, client_address, server)
|
||||
|
||||
def do_GET(self) -> None:
|
||||
"""Serve a GET request."""
|
||||
parts: list[str] = self.path.split("/")
|
||||
parts: List[str] = self.path.split("/")
|
||||
if not (len(parts) == 5 and not parts[0] and parts[1] == "tiles"):
|
||||
return
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import json
|
|||
import logging
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
from map_machine import (
|
||||
__author__,
|
||||
|
@ -54,7 +55,7 @@ class TaginfoProjectFile:
|
|||
):
|
||||
key: str = list(matcher.tags.keys())[0]
|
||||
value: str = matcher.tags[key]
|
||||
ids: list[str] = [
|
||||
ids: List[str] = [
|
||||
(x if isinstance(x, str) else x["shape"])
|
||||
for x in matcher.shapes
|
||||
]
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
OSM address tag processing.
|
||||
"""
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
from typing import Any, Dict, List, Set
|
||||
|
||||
from colour import Color
|
||||
|
||||
|
@ -25,8 +25,8 @@ class Label:
|
|||
|
||||
|
||||
def get_address(
|
||||
tags: dict[str, Any], draw_captions_mode: str, processed: set[str]
|
||||
) -> list[str]:
|
||||
tags: Dict[str, Any], draw_captions_mode: str, processed: Set[str]
|
||||
) -> List[str]:
|
||||
"""
|
||||
Construct address text list from the tags.
|
||||
|
||||
|
@ -34,7 +34,7 @@ def get_address(
|
|||
:param draw_captions_mode: captions mode ("all", "main", or "no")
|
||||
:param processed: set of processed tag keys
|
||||
"""
|
||||
address: list[str] = []
|
||||
address: List[str] = []
|
||||
|
||||
if draw_captions_mode == "address":
|
||||
if "addr:postcode" in tags:
|
||||
|
@ -80,10 +80,10 @@ def format_frequency(value: str) -> str:
|
|||
return f"{value} "
|
||||
|
||||
|
||||
def get_text(tags: dict[str, Any], processed: set[str]) -> list[Label]:
|
||||
def get_text(tags: Dict[str, Any], processed: Set[str]) -> List[Label]:
|
||||
"""Get text representation of writable tags."""
|
||||
texts: list[Label] = []
|
||||
values: list[str] = []
|
||||
texts: List[Label] = []
|
||||
values: List[str] = []
|
||||
|
||||
if "voltage:primary" in tags:
|
||||
values.append(tags["voltage:primary"])
|
||||
|
|
|
@ -8,7 +8,7 @@ import logging
|
|||
import sys
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
import cairosvg
|
||||
import numpy as np
|
||||
|
@ -72,7 +72,7 @@ class Tile:
|
|||
lat_deg: np.ndarray = np.degrees(lat_rad)
|
||||
return np.array((lat_deg, lon_deg))
|
||||
|
||||
def get_boundary_box(self) -> tuple[np.ndarray, np.ndarray]:
|
||||
def get_boundary_box(self) -> Tuple[np.ndarray, np.ndarray]:
|
||||
"""
|
||||
Get geographical boundary box of the tile: north-west and south-east
|
||||
points.
|
||||
|
@ -191,11 +191,11 @@ class Tile:
|
|||
cairosvg.svg2png(file_obj=input_file, write_to=str(output_path))
|
||||
logging.info(f"SVG file is rasterized to {output_path}.")
|
||||
|
||||
def subdivide(self, zoom_level: int) -> list["Tile"]:
|
||||
def subdivide(self, zoom_level: int) -> List["Tile"]:
|
||||
"""Get subtiles of the tile."""
|
||||
assert zoom_level >= self.zoom_level
|
||||
|
||||
tiles: list["Tile"] = []
|
||||
tiles: List["Tile"] = []
|
||||
n: int = 2 ** (zoom_level - self.zoom_level)
|
||||
for i in range(n):
|
||||
for j in range(n):
|
||||
|
@ -214,7 +214,7 @@ class Tiles:
|
|||
Collection of tiles.
|
||||
"""
|
||||
|
||||
tiles: list[Tile]
|
||||
tiles: List[Tile]
|
||||
tile_1: Tile # Left top tile.
|
||||
tile_2: Tile # Right bottom tile.
|
||||
zoom_level: int # OpenStreetMap zoom level.
|
||||
|
@ -230,7 +230,7 @@ class Tiles:
|
|||
:param boundary_box: area to be covered by tiles
|
||||
:param zoom_level: zoom level in OpenStreetMap terminology
|
||||
"""
|
||||
tiles: list[Tile] = []
|
||||
tiles: List[Tile] = []
|
||||
tile_1: Tile = Tile.from_coordinates(
|
||||
boundary_box.get_left_top(), zoom_level
|
||||
)
|
||||
|
@ -327,7 +327,7 @@ class Tiles:
|
|||
for tile in self.tiles:
|
||||
x: int = tile.x - self.tile_1.x
|
||||
y: int = tile.y - self.tile_1.y
|
||||
area: tuple[int, int, int, int] = (
|
||||
area: Tuple[int, int, int, int] = (
|
||||
x * TILE_WIDTH,
|
||||
y * TILE_HEIGHT,
|
||||
(x + 1) * TILE_WIDTH,
|
||||
|
@ -413,7 +413,7 @@ class Tiles:
|
|||
|
||||
def subdivide(self, zoom_level: int) -> "Tiles":
|
||||
"""Get subtiles from tiles."""
|
||||
tiles: list[Tile] = []
|
||||
tiles: List[Tile] = []
|
||||
for tile in self.tiles:
|
||||
tiles += tile.subdivide(zoom_level)
|
||||
return Tiles(
|
||||
|
@ -429,9 +429,9 @@ class ScaleConfigurationException(Exception):
|
|||
"""Wrong configuration format."""
|
||||
|
||||
|
||||
def parse_zoom_level(zoom_level_specification: str) -> list[int]:
|
||||
def parse_zoom_level(zoom_level_specification: str) -> List[int]:
|
||||
"""Parse zoom level specification."""
|
||||
parts: list[str]
|
||||
parts: List[str]
|
||||
if "," in zoom_level_specification:
|
||||
parts = zoom_level_specification.split(",")
|
||||
else:
|
||||
|
@ -444,7 +444,7 @@ def parse_zoom_level(zoom_level_specification: str) -> list[int]:
|
|||
raise ScaleConfigurationException("Scale is too big.")
|
||||
return parsed_zoom_level
|
||||
|
||||
result: list[int] = []
|
||||
result: List[int] = []
|
||||
for part in parts:
|
||||
if "-" in part:
|
||||
from_, to = part.split("-")
|
||||
|
@ -463,7 +463,7 @@ def ui(options: argparse.Namespace) -> None:
|
|||
"""Simple user interface for tile generation."""
|
||||
directory: Path = workspace.get_tile_path()
|
||||
|
||||
zoom_levels: list[int] = parse_zoom_level(options.zoom)
|
||||
zoom_levels: List[int] = parse_zoom_level(options.zoom)
|
||||
min_zoom_level: int = min(zoom_levels)
|
||||
|
||||
if options.input_file_name:
|
||||
|
@ -478,7 +478,7 @@ def ui(options: argparse.Namespace) -> None:
|
|||
)
|
||||
tiles.draw(directory, Path(options.cache), configuration, osm_data)
|
||||
elif options.coordinates:
|
||||
coordinates: list[float] = list(
|
||||
coordinates: List[float] = list(
|
||||
map(float, options.coordinates.strip().split(","))
|
||||
)
|
||||
min_tile: Tile = Tile.from_coordinates(
|
||||
|
|
|
@ -3,6 +3,7 @@ Command-line user interface.
|
|||
"""
|
||||
import argparse
|
||||
import sys
|
||||
from typing import Dict, List
|
||||
|
||||
from map_machine import __version__
|
||||
from map_machine.map_configuration import BuildingMode, DrawingMode, LabelMode
|
||||
|
@ -14,7 +15,7 @@ __email__ = "me@enzet.ru"
|
|||
BOXES: str = " ▏▎▍▌▋▊▉"
|
||||
BOXES_LENGTH: int = len(BOXES)
|
||||
|
||||
COMMANDS: dict[str, list[str]] = {
|
||||
COMMANDS: Dict[str, List[str]] = {
|
||||
"render": ["render", "-b", "10.000,20.000,10.001,20.001"],
|
||||
"render_with_tooltips": [
|
||||
"render",
|
||||
|
@ -29,7 +30,7 @@ COMMANDS: dict[str, list[str]] = {
|
|||
}
|
||||
|
||||
|
||||
def parse_arguments(args: list[str]) -> argparse.Namespace:
|
||||
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"
|
||||
|
@ -117,9 +118,15 @@ def add_map_arguments(parser: argparse.ArgumentParser) -> None:
|
|||
parser.add_argument(
|
||||
"--show-tooltips",
|
||||
help="add tooltips with tags for icons in SVG files",
|
||||
action=argparse.BooleanOptionalAction,
|
||||
action="store_true",
|
||||
default=False,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-show-tooltips",
|
||||
dest="show_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 "
|
||||
|
@ -256,24 +263,42 @@ def add_mapcss_arguments(parser: argparse.ArgumentParser) -> None:
|
|||
"""Add arguments for mapcss command."""
|
||||
parser.add_argument(
|
||||
"--icons",
|
||||
action=argparse.BooleanOptionalAction,
|
||||
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=argparse.BooleanOptionalAction,
|
||||
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=argparse.BooleanOptionalAction,
|
||||
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",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-lifecycle",
|
||||
dest="lifecycle",
|
||||
action="store_false",
|
||||
help="don't add icons for lifecycle tags",
|
||||
)
|
||||
|
||||
|
||||
def progress_bar(
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
"""
|
||||
Vector utility.
|
||||
"""
|
||||
from typing import List
|
||||
|
||||
import numpy as np
|
||||
|
||||
__author__ = "Sergey Vartanov"
|
||||
|
@ -45,12 +47,12 @@ class Polyline:
|
|||
List of connected points.
|
||||
"""
|
||||
|
||||
def __init__(self, points: list[np.ndarray]) -> None:
|
||||
self.points: list[np.ndarray] = points
|
||||
def __init__(self, points: List[np.ndarray]) -> None:
|
||||
self.points: List[np.ndarray] = points
|
||||
|
||||
def get_path(self, parallel_offset: float = 0) -> str:
|
||||
"""Construct SVG path commands."""
|
||||
points: list[np.ndarray]
|
||||
points: List[np.ndarray]
|
||||
try:
|
||||
points = (
|
||||
LineString(self.points).parallel_offset(parallel_offset).coords
|
||||
|
|
2
setup.py
2
setup.py
|
@ -49,7 +49,7 @@ setup(
|
|||
"scheme/default.yml",
|
||||
],
|
||||
},
|
||||
python_requires=">=3.9",
|
||||
python_requires=">=3.8",
|
||||
install_requires=[
|
||||
"CairoSVG>=2.5.0",
|
||||
"colour>=0.1.5",
|
||||
|
|
|
@ -7,13 +7,15 @@ from subprocess import PIPE, Popen
|
|||
__author__ = "Sergey Vartanov"
|
||||
__email__ = "me@enzet.ru"
|
||||
|
||||
from typing import List
|
||||
|
||||
from xml.etree import ElementTree
|
||||
from xml.etree.ElementTree import Element
|
||||
|
||||
from map_machine.ui import COMMANDS
|
||||
|
||||
|
||||
def error_run(arguments: list[str], message: bytes) -> None:
|
||||
def error_run(arguments: List[str], message: bytes) -> None:
|
||||
"""Run command that should fail and check error message."""
|
||||
p = Popen(["map-machine"] + arguments, stderr=PIPE)
|
||||
_, error = p.communicate()
|
||||
|
@ -21,7 +23,7 @@ def error_run(arguments: list[str], message: bytes) -> None:
|
|||
assert error == message
|
||||
|
||||
|
||||
def run(arguments: list[str], message: bytes) -> None:
|
||||
def run(arguments: List[str], message: bytes) -> None:
|
||||
"""Run command that should fail and check error message."""
|
||||
p = Popen(["map-machine"] + arguments, stderr=PIPE)
|
||||
_, error = p.communicate()
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
"""
|
||||
Test icon generation for nodes.
|
||||
"""
|
||||
from typing import Dict, Set
|
||||
|
||||
import pytest
|
||||
|
||||
from map_machine.grid import IconCollection
|
||||
|
@ -32,9 +34,9 @@ def test_icons_by_name(init_collection: IconCollection) -> None:
|
|||
init_collection.draw_icons(workspace.get_icons_by_name_path(), by_name=True)
|
||||
|
||||
|
||||
def get_icon(tags: dict[str, str]) -> IconSet:
|
||||
def get_icon(tags: Dict[str, str]) -> IconSet:
|
||||
"""Construct icon from tags."""
|
||||
processed: set[str] = set()
|
||||
processed: Set[str] = set()
|
||||
icon, _ = SCHEME.get_icon(SHAPE_EXTRACTOR, tags, processed)
|
||||
return icon
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
"""
|
||||
Test label generation for nodes.
|
||||
"""
|
||||
from typing import Dict, List, Set
|
||||
|
||||
from map_machine.text import Label
|
||||
from tests import SCHEME
|
||||
|
||||
|
@ -8,9 +10,9 @@ __author__ = "Sergey Vartanov"
|
|||
__email__ = "me@enzet.ru"
|
||||
|
||||
|
||||
def construct_labels(tags: dict[str, str]) -> list[Label]:
|
||||
def construct_labels(tags: Dict[str, str]) -> List[Label]:
|
||||
"""Construct labels from OSM node tags."""
|
||||
processed: set[str] = set()
|
||||
processed: Set[str] = set()
|
||||
return SCHEME.construct_text(tags, "all", processed)
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue