Change scheme format; support pathlib.
Change name documentation as well.
6
.gitignore
vendored
|
@ -22,8 +22,10 @@ missed_tags.yml
|
||||||
|
|
||||||
# Cache
|
# Cache
|
||||||
|
|
||||||
map/ # OSM XML files
|
cache/
|
||||||
|
map/
|
||||||
|
|
||||||
# Generated files
|
# Generated files
|
||||||
|
|
||||||
icon_set/ # Generated SVG icon files
|
icon_set/
|
||||||
|
out/
|
||||||
|
|
BIN
doc/grid.png
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
BIN
doc/power.png
Before Width: | Height: | Size: 116 KiB After Width: | Height: | Size: 116 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 652 B |
Before Width: | Height: | Size: 145 KiB After Width: | Height: | Size: 144 KiB |
BIN
doc/trees.png
Before Width: | Height: | Size: 167 KiB After Width: | Height: | Size: 169 KiB |
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 68 KiB |
43
roentgen.py
|
@ -7,6 +7,7 @@ import argparse
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import List
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import svgwrite
|
import svgwrite
|
||||||
|
@ -15,7 +16,7 @@ from roentgen import ui
|
||||||
from roentgen.constructor import Constructor
|
from roentgen.constructor import Constructor
|
||||||
from roentgen.flinger import Flinger
|
from roentgen.flinger import Flinger
|
||||||
from roentgen.grid import draw_all_icons
|
from roentgen.grid import draw_all_icons
|
||||||
from roentgen.icon import ShapeExtractor, ShapeConfiguration
|
from roentgen.icon import ShapeExtractor
|
||||||
from roentgen.mapper import (
|
from roentgen.mapper import (
|
||||||
AUTHOR_MODE, CREATION_TIME_MODE, ICONS_FILE_NAME, Painter, TAGS_FILE_NAME,
|
AUTHOR_MODE, CREATION_TIME_MODE, ICONS_FILE_NAME, Painter, TAGS_FILE_NAME,
|
||||||
check_level_number, check_level_overground
|
check_level_number, check_level_overground
|
||||||
|
@ -38,22 +39,26 @@ def main(argv) -> None:
|
||||||
if not options:
|
if not options:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
input_file_names: List[Path]
|
||||||
|
|
||||||
if options.input_file_name:
|
if options.input_file_name:
|
||||||
input_file_name = options.input_file_name
|
input_file_names = list(map(Path, options.input_file_name))
|
||||||
else:
|
else:
|
||||||
content = get_osm(options.boundary_box)
|
content = get_osm(options.boundary_box)
|
||||||
if not content:
|
if not content:
|
||||||
ui.error("cannot download OSM data")
|
ui.error("cannot download OSM data")
|
||||||
input_file_name = [os.path.join("map", options.boundary_box + ".osm")]
|
input_file_names = ["map" / Path(options.boundary_box + ".osm")]
|
||||||
|
|
||||||
scheme: Scheme = Scheme(TAGS_FILE_NAME)
|
scheme: Scheme = Scheme(Path(TAGS_FILE_NAME))
|
||||||
|
min_: np.array
|
||||||
|
max_: np.array
|
||||||
|
|
||||||
if input_file_name[0].endswith(".json"):
|
if input_file_names[0].name.endswith(".json"):
|
||||||
reader: OverpassReader = OverpassReader()
|
reader: OverpassReader = OverpassReader()
|
||||||
reader.parse_json_file(input_file_name[0])
|
reader.parse_json_file(input_file_names[0])
|
||||||
map_ = reader.map_
|
map_ = reader.map_
|
||||||
min1 = np.array((map_.boundary_box[0].min_, map_.boundary_box[1].min_))
|
min_ = np.array((map_.boundary_box[0].min_, map_.boundary_box[1].min_))
|
||||||
max1 = np.array((map_.boundary_box[0].max_, map_.boundary_box[1].max_))
|
max_ = np.array((map_.boundary_box[0].max_, map_.boundary_box[1].max_))
|
||||||
else:
|
else:
|
||||||
|
|
||||||
boundary_box = list(map(float, options.boundary_box.split(',')))
|
boundary_box = list(map(float, options.boundary_box.split(',')))
|
||||||
|
@ -65,19 +70,19 @@ def main(argv) -> None:
|
||||||
|
|
||||||
osm_reader = OSMReader()
|
osm_reader = OSMReader()
|
||||||
|
|
||||||
for file_name in input_file_name:
|
for file_name in input_file_names:
|
||||||
if not os.path.isfile(file_name):
|
if not file_name.is_file():
|
||||||
print("Fatal: no such file: " + file_name + ".")
|
print(f"Fatal: no such file: {file_name}.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
osm_reader.parse_osm_file(file_name, full=full)
|
osm_reader.parse_osm_file(file_name, full=full)
|
||||||
|
|
||||||
map_: Map = osm_reader.map_
|
map_: Map = osm_reader.map_
|
||||||
|
|
||||||
min1: np.array = np.array((boundary_box[1], boundary_box[0]))
|
min_ = np.array((boundary_box[1], boundary_box[0]))
|
||||||
max1: np.array = np.array((boundary_box[3], boundary_box[2]))
|
max_ = np.array((boundary_box[3], boundary_box[2]))
|
||||||
|
|
||||||
flinger: Flinger = Flinger(MinMax(min1, max1), options.scale)
|
flinger: Flinger = Flinger(MinMax(min_, max_), options.scale)
|
||||||
size: np.array = flinger.size
|
size: np.array = flinger.size
|
||||||
|
|
||||||
svg: svgwrite.Drawing = (
|
svg: svgwrite.Drawing = (
|
||||||
|
@ -130,11 +135,13 @@ def draw_element(target: str, tags_description: str):
|
||||||
comma, key from value is separated by equals sign.
|
comma, key from value is separated by equals sign.
|
||||||
"""
|
"""
|
||||||
tags = dict([x.split("=") for x in tags_description.split(",")])
|
tags = dict([x.split("=") for x in tags_description.split(",")])
|
||||||
scheme = Scheme("scheme/default.yml")
|
scheme = Scheme(Path("scheme/default.yml"))
|
||||||
extractor = ShapeExtractor("icons/icons.svg", Path("icons/config.json"))
|
extractor = ShapeExtractor(
|
||||||
|
Path("icons/icons.svg"), Path("icons/config.json")
|
||||||
|
)
|
||||||
icon, priority = scheme.get_icon(extractor, tags)
|
icon, priority = scheme.get_icon(extractor, tags)
|
||||||
is_for_node: bool = target == "node"
|
is_for_node: bool = target == "node"
|
||||||
labels = scheme.construct_text(tags, True)
|
labels = scheme.construct_text(tags, "all")
|
||||||
point = Point(
|
point = Point(
|
||||||
icon, labels, tags, np.array((32, 32)), None, is_for_node=is_for_node,
|
icon, labels, tags, np.array((32, 32)), None, is_for_node=is_for_node,
|
||||||
draw_outline=is_for_node
|
draw_outline=is_for_node
|
||||||
|
@ -154,7 +161,7 @@ def draw_element(target: str, tags_description: str):
|
||||||
svg.write(open("test_icon.svg", "w+"))
|
svg.write(open("test_icon.svg", "w+"))
|
||||||
|
|
||||||
|
|
||||||
def draw_grid():
|
def draw_grid() -> None:
|
||||||
"""
|
"""
|
||||||
Draw all possible icon shapes combinations as grid.
|
Draw all possible icon shapes combinations as grid.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
"""
|
"""
|
||||||
Color utility.
|
Color utility.
|
||||||
|
|
||||||
Author: Sergey Vartanov (me@enzet.ru)
|
|
||||||
"""
|
"""
|
||||||
from typing import Any, List
|
from typing import Any, List
|
||||||
|
|
||||||
|
@ -9,6 +7,9 @@ from colour import Color
|
||||||
|
|
||||||
from roentgen.util import MinMax
|
from roentgen.util import MinMax
|
||||||
|
|
||||||
|
__author__ = "Sergey Vartanov"
|
||||||
|
__email__ = "me@enzet.ru"
|
||||||
|
|
||||||
|
|
||||||
def is_bright(color: Color) -> bool:
|
def is_bright(color: Color) -> bool:
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
"""
|
"""
|
||||||
Construct Röntgen nodes and ways.
|
Construct Röntgen nodes and ways.
|
||||||
|
|
||||||
Author: Sergey Vartanov (me@enzet.ru).
|
|
||||||
"""
|
"""
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
@ -23,6 +21,9 @@ from roentgen.point import Point
|
||||||
from roentgen.scheme import DEFAULT_COLOR, LineStyle, Scheme
|
from roentgen.scheme import DEFAULT_COLOR, LineStyle, Scheme
|
||||||
from roentgen.util import MinMax
|
from roentgen.util import MinMax
|
||||||
|
|
||||||
|
__author__ = "Sergey Vartanov"
|
||||||
|
__email__ = "me@enzet.ru"
|
||||||
|
|
||||||
DEBUG: bool = False
|
DEBUG: bool = False
|
||||||
TIME_COLOR_SCALE: List[Color] = [
|
TIME_COLOR_SCALE: List[Color] = [
|
||||||
Color("#581845"), Color("#900C3F"), Color("#C70039"), Color("#FF5733"),
|
Color("#581845"), Color("#900C3F"), Color("#C70039"), Color("#FF5733"),
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
"""
|
"""
|
||||||
Direction tag support.
|
Direction tag support.
|
||||||
|
|
||||||
Author: Sergey Vartanov (me@enzet.ru).
|
|
||||||
"""
|
"""
|
||||||
from typing import Iterator, List, Optional, Union
|
from typing import Iterator, List, Optional, Union
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from portolan import middle
|
from portolan import middle
|
||||||
|
|
||||||
|
__author__ = "Sergey Vartanov"
|
||||||
|
__email__ = "me@enzet.ru"
|
||||||
|
|
||||||
SVGPath = Union[float, str, np.array]
|
SVGPath = Union[float, str, np.array]
|
||||||
|
|
||||||
SHIFT: float = -np.pi / 2
|
SHIFT: float = -np.pi / 2
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
"""
|
"""
|
||||||
Author: Sergey Vartanov (me@enzet.ru)
|
|
||||||
|
|
||||||
Geo projection.
|
Geo projection.
|
||||||
"""
|
"""
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
@ -9,6 +7,9 @@ import numpy as np
|
||||||
|
|
||||||
from roentgen.util import MinMax
|
from roentgen.util import MinMax
|
||||||
|
|
||||||
|
__author__ = "Sergey Vartanov"
|
||||||
|
__email__ = "me@enzet.ru"
|
||||||
|
|
||||||
EQUATOR_LENGTH: float = 40_075_017 # (in meters)
|
EQUATOR_LENGTH: float = 40_075_017 # (in meters)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
"""
|
"""
|
||||||
Icon grid drawing.
|
Icon grid drawing.
|
||||||
|
|
||||||
Author: Sergey Vartanov (me@enzet.ru).
|
|
||||||
"""
|
"""
|
||||||
from os.path import join
|
from os.path import join
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
@ -14,6 +12,9 @@ from svgwrite import Drawing
|
||||||
from roentgen.icon import Icon, ShapeExtractor, ShapeSpecification
|
from roentgen.icon import Icon, ShapeExtractor, ShapeSpecification
|
||||||
from roentgen.scheme import Scheme
|
from roentgen.scheme import Scheme
|
||||||
|
|
||||||
|
__author__ = "Sergey Vartanov"
|
||||||
|
__email__ = "me@enzet.ru"
|
||||||
|
|
||||||
|
|
||||||
def draw_all_icons(
|
def draw_all_icons(
|
||||||
output_file_name: str, output_directory: str, columns: int = 16,
|
output_file_name: str, output_directory: str, columns: int = 16,
|
||||||
|
@ -31,14 +32,13 @@ def draw_all_icons(
|
||||||
:param background_color: background color
|
:param background_color: background color
|
||||||
:param color: icon color
|
:param color: icon color
|
||||||
"""
|
"""
|
||||||
tags_file_name: str = "scheme/default.yml"
|
scheme: Scheme = Scheme(Path("scheme/default.yml"))
|
||||||
scheme: Scheme = Scheme(tags_file_name)
|
|
||||||
|
|
||||||
icons: List[Icon] = []
|
icons: List[Icon] = []
|
||||||
|
|
||||||
icons_file_name: str = "icons/icons.svg"
|
icons_file_name: str = "icons/icons.svg"
|
||||||
extractor: ShapeExtractor = ShapeExtractor(
|
extractor: ShapeExtractor = ShapeExtractor(
|
||||||
icons_file_name, Path("icons/config.json")
|
Path(icons_file_name), Path("icons/config.json")
|
||||||
)
|
)
|
||||||
|
|
||||||
def add() -> None:
|
def add() -> None:
|
||||||
|
@ -54,32 +54,33 @@ def draw_all_icons(
|
||||||
if constructed_icon not in icons:
|
if constructed_icon not in icons:
|
||||||
icons.append(constructed_icon)
|
icons.append(constructed_icon)
|
||||||
|
|
||||||
for element in scheme.icons: # type: Dict[str, Any]
|
for group in scheme.icons:
|
||||||
for key in ["icon", "add_icon"]:
|
for element in group["tags"]: # type: Dict[str, Any]
|
||||||
if key in element:
|
for key in ["icon", "add_icon"]:
|
||||||
current_set = element[key]
|
if key in element:
|
||||||
add()
|
current_set = element[key]
|
||||||
if "over_icon" not in element:
|
add()
|
||||||
continue
|
if "over_icon" not in element:
|
||||||
if "under_icon" in element:
|
continue
|
||||||
|
if "under_icon" in element:
|
||||||
|
for icon_id in element["under_icon"]: # type: str
|
||||||
|
current_set = set([icon_id] + element["over_icon"])
|
||||||
|
add()
|
||||||
|
if not ("under_icon" in element and "with_icon" in element):
|
||||||
|
continue
|
||||||
for icon_id in element["under_icon"]: # type: str
|
for icon_id in element["under_icon"]: # type: str
|
||||||
current_set = set([icon_id] + element["over_icon"])
|
for icon_2_id in element["with_icon"]: # type: str
|
||||||
add()
|
current_set: Set[str] = set(
|
||||||
if not ("under_icon" in element and "with_icon" in element):
|
[icon_id] + [icon_2_id] + element["over_icon"])
|
||||||
continue
|
add()
|
||||||
for icon_id in element["under_icon"]: # type: str
|
for icon_2_id in element["with_icon"]: # type: str
|
||||||
for icon_2_id in element["with_icon"]: # type: str
|
for icon_3_id in element["with_icon"]: # type: str
|
||||||
current_set: Set[str] = set(
|
current_set = set(
|
||||||
[icon_id] + [icon_2_id] + element["over_icon"])
|
[icon_id] + [icon_2_id] + [icon_3_id] +
|
||||||
add()
|
element["over_icon"])
|
||||||
for icon_2_id in element["with_icon"]: # type: str
|
if (icon_2_id != icon_3_id and icon_2_id != icon_id and
|
||||||
for icon_3_id in element["with_icon"]: # type: str
|
icon_3_id != icon_id):
|
||||||
current_set = set(
|
add()
|
||||||
[icon_id] + [icon_2_id] + [icon_3_id] +
|
|
||||||
element["over_icon"])
|
|
||||||
if (icon_2_id != icon_3_id and icon_2_id != icon_id and
|
|
||||||
icon_3_id != icon_id):
|
|
||||||
add()
|
|
||||||
|
|
||||||
specified_ids: Set[str] = set()
|
specified_ids: Set[str] = set()
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
"""
|
"""
|
||||||
Extract icons from SVG file.
|
Extract icons from SVG file.
|
||||||
|
|
||||||
Author: Sergey Vartanov (me@enzet.ru).
|
|
||||||
"""
|
"""
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
@ -19,6 +17,9 @@ from svgwrite.path import Path as SvgPath
|
||||||
from roentgen.color import is_bright
|
from roentgen.color import is_bright
|
||||||
from roentgen.ui import error
|
from roentgen.ui import error
|
||||||
|
|
||||||
|
__author__ = "Sergey Vartanov"
|
||||||
|
__email__ = "me@enzet.ru"
|
||||||
|
|
||||||
DEFAULT_COLOR: Color = Color("#444444")
|
DEFAULT_COLOR: Color = Color("#444444")
|
||||||
DEFAULT_SHAPE_ID: str = "default"
|
DEFAULT_SHAPE_ID: str = "default"
|
||||||
DEFAULT_SMALL_SHAPE_ID: str = "default_small"
|
DEFAULT_SMALL_SHAPE_ID: str = "default_small"
|
||||||
|
@ -94,7 +95,7 @@ class ShapeExtractor:
|
||||||
Shape is a single path with "id" attribute that aligned to 16×16 grid.
|
Shape is a single path with "id" attribute that aligned to 16×16 grid.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, svg_file_name: str, configuration_file_name: Path):
|
def __init__(self, svg_file_name: Path, configuration_file_name: Path):
|
||||||
"""
|
"""
|
||||||
:param svg_file_name: input SVG file name with icons. File may contain
|
:param svg_file_name: input SVG file name with icons. File may contain
|
||||||
any other irrelevant graphics.
|
any other irrelevant graphics.
|
||||||
|
@ -102,7 +103,7 @@ class ShapeExtractor:
|
||||||
self.configuration = ShapeConfiguration(configuration_file_name)
|
self.configuration = ShapeConfiguration(configuration_file_name)
|
||||||
self.shapes: Dict[str, Shape] = {}
|
self.shapes: Dict[str, Shape] = {}
|
||||||
|
|
||||||
with open(svg_file_name) as input_file:
|
with svg_file_name.open() as input_file:
|
||||||
content: Document = parse(input_file)
|
content: Document = parse(input_file)
|
||||||
for element in content.childNodes: # type: Element
|
for element in content.childNodes: # type: Element
|
||||||
if element.nodeName != "svg":
|
if element.nodeName != "svg":
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
"""
|
"""
|
||||||
Simple OpenStreetMap renderer.
|
Simple OpenStreetMap renderer.
|
||||||
|
|
||||||
Author: Sergey Vartanov (me@enzet.ru).
|
|
||||||
"""
|
"""
|
||||||
from typing import Any, Dict
|
from typing import Any, Dict
|
||||||
|
|
||||||
|
@ -21,6 +19,9 @@ from roentgen.osm_reader import Map
|
||||||
from roentgen.point import Occupied, Point
|
from roentgen.point import Occupied, Point
|
||||||
from roentgen.scheme import Scheme
|
from roentgen.scheme import Scheme
|
||||||
|
|
||||||
|
__author__ = "Sergey Vartanov"
|
||||||
|
__email__ = "me@enzet.ru"
|
||||||
|
|
||||||
ICONS_FILE_NAME: str = "icons/icons.svg"
|
ICONS_FILE_NAME: str = "icons/icons.svg"
|
||||||
TAGS_FILE_NAME: str = "scheme/default.yml"
|
TAGS_FILE_NAME: str = "scheme/default.yml"
|
||||||
MISSING_TAGS_FILE_NAME: str = "missing_tags.yml"
|
MISSING_TAGS_FILE_NAME: str = "missing_tags.yml"
|
||||||
|
|
|
@ -1,18 +1,19 @@
|
||||||
"""
|
"""
|
||||||
Getting OpenStreetMap data from the web.
|
Getting OpenStreetMap data from the web.
|
||||||
|
|
||||||
Author: Sergey Vartanov (me@enzet.ru).
|
|
||||||
"""
|
"""
|
||||||
import os
|
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
import urllib
|
import urllib
|
||||||
|
from pathlib import Path
|
||||||
from typing import Dict, Optional
|
from typing import Dict, Optional
|
||||||
|
|
||||||
import urllib3
|
import urllib3
|
||||||
|
|
||||||
from roentgen.ui import error
|
from roentgen.ui import error
|
||||||
|
|
||||||
|
__author__ = "Sergey Vartanov"
|
||||||
|
__email__ = "me@enzet.ru"
|
||||||
|
|
||||||
|
|
||||||
def get_osm(boundary_box: str, to_update: bool = False) -> Optional[str]:
|
def get_osm(boundary_box: str, to_update: bool = False) -> Optional[str]:
|
||||||
"""
|
"""
|
||||||
|
@ -21,10 +22,10 @@ def get_osm(boundary_box: str, to_update: bool = False) -> Optional[str]:
|
||||||
:param boundary_box: borders of the map part to download
|
:param boundary_box: borders of the map part to download
|
||||||
:param to_update: update cache files
|
:param to_update: update cache files
|
||||||
"""
|
"""
|
||||||
result_file_name = os.path.join("map", boundary_box + ".osm")
|
result_file_name: Path = "map" / Path(boundary_box + ".osm")
|
||||||
|
|
||||||
if not to_update and os.path.isfile(result_file_name):
|
if not to_update and result_file_name.is_file():
|
||||||
return open(result_file_name).read()
|
return result_file_name.open().read()
|
||||||
|
|
||||||
matcher = re.match(
|
matcher = re.match(
|
||||||
"(?P<left>[0-9.-]*),(?P<bottom>[0-9.-]*)," +
|
"(?P<left>[0-9.-]*),(?P<bottom>[0-9.-]*)," +
|
||||||
|
@ -58,7 +59,7 @@ def get_osm(boundary_box: str, to_update: bool = False) -> Optional[str]:
|
||||||
"api.openstreetmap.org/api/0.6/map",
|
"api.openstreetmap.org/api/0.6/map",
|
||||||
{"bbox": boundary_box}, is_secure=True)
|
{"bbox": boundary_box}, is_secure=True)
|
||||||
|
|
||||||
open(result_file_name, "w+").write(content.decode("utf-8"))
|
result_file_name.open("w+").write(content.decode("utf-8"))
|
||||||
|
|
||||||
return content.decode("utf-8")
|
return content.decode("utf-8")
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
"""
|
"""
|
||||||
Reading OpenStreetMap data from XML file.
|
Reading OpenStreetMap data from XML file.
|
||||||
|
|
||||||
Author: Sergey Vartanov (me@enzet.ru).
|
|
||||||
"""
|
"""
|
||||||
import json
|
import json
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from pathlib import Path
|
||||||
from typing import Any, Dict, List, Optional, Set, Union
|
from typing import Any, Dict, List, Optional, Set, Union
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
@ -12,6 +11,9 @@ import numpy as np
|
||||||
from roentgen.ui import progress_bar
|
from roentgen.ui import progress_bar
|
||||||
from roentgen.util import MinMax
|
from roentgen.util import MinMax
|
||||||
|
|
||||||
|
__author__ = "Sergey Vartanov"
|
||||||
|
__email__ = "me@enzet.ru"
|
||||||
|
|
||||||
OSM_TIME_PATTERN: str = "%Y-%m-%dT%H:%M:%SZ"
|
OSM_TIME_PATTERN: str = "%Y-%m-%dT%H:%M:%SZ"
|
||||||
|
|
||||||
|
|
||||||
|
@ -67,7 +69,8 @@ class OSMNode(Tagged):
|
||||||
self.visible = get_value("visible", text)
|
self.visible = get_value("visible", text)
|
||||||
self.changeset = get_value("changeset", text)
|
self.changeset = get_value("changeset", text)
|
||||||
self.timestamp = datetime.strptime(
|
self.timestamp = datetime.strptime(
|
||||||
get_value("timestamp", text), OSM_TIME_PATTERN)
|
get_value("timestamp", text), OSM_TIME_PATTERN
|
||||||
|
)
|
||||||
self.user = get_value("user", text)
|
self.user = get_value("user", text)
|
||||||
self.uid = get_value("uid", text)
|
self.uid = get_value("uid", text)
|
||||||
|
|
||||||
|
@ -296,11 +299,11 @@ class OverpassReader:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.map_ = Map()
|
self.map_ = Map()
|
||||||
|
|
||||||
def parse_json_file(self, file_name: str) -> Map:
|
def parse_json_file(self, file_name: Path) -> Map:
|
||||||
"""
|
"""
|
||||||
Parse JSON structure from the file and construct map.
|
Parse JSON structure from the file and construct map.
|
||||||
"""
|
"""
|
||||||
with open(file_name) as input_file:
|
with file_name.open() as input_file:
|
||||||
structure = json.load(input_file)
|
structure = json.load(input_file)
|
||||||
|
|
||||||
node_map = {}
|
node_map = {}
|
||||||
|
@ -332,7 +335,7 @@ class OSMReader:
|
||||||
self.map_ = Map()
|
self.map_ = Map()
|
||||||
|
|
||||||
def parse_osm_file(
|
def parse_osm_file(
|
||||||
self, file_name: str, parse_nodes: bool = True,
|
self, file_name: Path, parse_nodes: bool = True,
|
||||||
parse_ways: bool = True, parse_relations: bool = True,
|
parse_ways: bool = True, parse_relations: bool = True,
|
||||||
full: bool = False
|
full: bool = False
|
||||||
) -> Map:
|
) -> Map:
|
||||||
|
@ -341,7 +344,7 @@ class OSMReader:
|
||||||
|
|
||||||
:param file_name: input OSM XML file name
|
:param file_name: input OSM XML file name
|
||||||
"""
|
"""
|
||||||
with open(file_name) as input_file:
|
with file_name.open() as input_file:
|
||||||
lines_number: int = sum(1 for _ in input_file)
|
lines_number: int = sum(1 for _ in input_file)
|
||||||
|
|
||||||
print(f"Parsing OSM file {file_name}...")
|
print(f"Parsing OSM file {file_name}...")
|
||||||
|
@ -349,7 +352,7 @@ class OSMReader:
|
||||||
|
|
||||||
element: Optional[Union[OSMNode, OSMWay, OSMRelation]] = None
|
element: Optional[Union[OSMNode, OSMWay, OSMRelation]] = None
|
||||||
|
|
||||||
with open(file_name) as input_file:
|
with file_name.open() as input_file:
|
||||||
for line in input_file.readlines(): # type: str
|
for line in input_file.readlines(): # type: str
|
||||||
|
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
|
|
|
@ -11,6 +11,9 @@ from roentgen.icon import Icon, IconSet
|
||||||
from roentgen.osm_reader import Tagged
|
from roentgen.osm_reader import Tagged
|
||||||
from roentgen.text import Label
|
from roentgen.text import Label
|
||||||
|
|
||||||
|
__author__ = "Sergey Vartanov"
|
||||||
|
__email__ = "me@enzet.ru"
|
||||||
|
|
||||||
DEFAULT_FONT: str = "Roboto"
|
DEFAULT_FONT: str = "Roboto"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
"""
|
"""
|
||||||
Röntgen drawing scheme.
|
Röntgen drawing scheme.
|
||||||
|
|
||||||
Author: Sergey Vartanov (me@enzet.ru).
|
|
||||||
"""
|
"""
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
from pathlib import Path
|
||||||
from typing import Any, Dict, List, Optional, Set, Tuple, Union
|
from typing import Any, Dict, List, Optional, Set, Tuple, Union
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
|
@ -17,6 +16,9 @@ from roentgen.icon import (
|
||||||
)
|
)
|
||||||
from roentgen.text import Label, get_address, get_text
|
from roentgen.text import Label, get_address, get_text
|
||||||
|
|
||||||
|
__author__ = "Sergey Vartanov"
|
||||||
|
__email__ = "me@enzet.ru"
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class LineStyle:
|
class LineStyle:
|
||||||
|
@ -103,12 +105,12 @@ class Scheme:
|
||||||
|
|
||||||
Specifies map colors and rules to draw icons for OpenStreetMap tags.
|
Specifies map colors and rules to draw icons for OpenStreetMap tags.
|
||||||
"""
|
"""
|
||||||
def __init__(self, file_name: str):
|
def __init__(self, file_name: Path):
|
||||||
"""
|
"""
|
||||||
:param file_name: scheme file name with tags, colors, and tag key
|
:param file_name: name of the scheme file with tags, colors, and tag key
|
||||||
specification
|
specification
|
||||||
"""
|
"""
|
||||||
with open(file_name) as input_file:
|
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)
|
input_file.read(), Loader=yaml.FullLoader)
|
||||||
|
|
||||||
|
@ -198,45 +200,49 @@ class Scheme:
|
||||||
processed: Set[str] = set()
|
processed: Set[str] = set()
|
||||||
priority: int = 0
|
priority: int = 0
|
||||||
|
|
||||||
for index, matcher in enumerate(self.icons):
|
index: int = 0
|
||||||
index: int
|
|
||||||
matcher: Dict[str, Any]
|
for group in self.icons:
|
||||||
matched: bool = is_matched(matcher, tags)
|
for matcher in group["tags"]:
|
||||||
matcher_tags: Set[str] = matcher["tags"].keys()
|
matcher: Dict[str, Any]
|
||||||
if not matched:
|
matched: bool = is_matched(matcher, tags)
|
||||||
continue
|
matcher_tags: Set[str] = matcher["tags"].keys()
|
||||||
priority = len(self.icons) - index
|
if not matched:
|
||||||
if "draw" in matcher and not matcher["draw"]:
|
continue
|
||||||
processed |= set(matcher_tags)
|
priority = len(self.icons) - index
|
||||||
if "icon" in matcher:
|
if "draw" in matcher and not matcher["draw"]:
|
||||||
main_icon = Icon([
|
processed |= set(matcher_tags)
|
||||||
ShapeSpecification.from_structure(x, icon_extractor, self)
|
if "icon" in matcher:
|
||||||
for x in matcher["icon"]
|
main_icon = Icon([
|
||||||
])
|
ShapeSpecification.from_structure(x, icon_extractor, self)
|
||||||
processed |= set(matcher_tags)
|
for x in matcher["icon"]
|
||||||
if "over_icon" in matcher:
|
|
||||||
if main_icon:
|
|
||||||
main_icon.add_specifications([
|
|
||||||
ShapeSpecification.from_structure(
|
|
||||||
x, icon_extractor, self
|
|
||||||
)
|
|
||||||
for x in matcher["over_icon"]
|
|
||||||
])
|
])
|
||||||
|
processed |= set(matcher_tags)
|
||||||
|
if "over_icon" in matcher:
|
||||||
|
if main_icon:
|
||||||
|
main_icon.add_specifications([
|
||||||
|
ShapeSpecification.from_structure(
|
||||||
|
x, icon_extractor, self
|
||||||
|
)
|
||||||
|
for x in matcher["over_icon"]
|
||||||
|
])
|
||||||
|
for key in matcher_tags:
|
||||||
|
processed.add(key)
|
||||||
|
if "add_icon" in matcher:
|
||||||
|
extra_icons += [Icon([
|
||||||
|
ShapeSpecification.from_structure(
|
||||||
|
x, icon_extractor, self, Color("#888888")
|
||||||
|
)
|
||||||
|
for x in matcher["add_icon"]
|
||||||
|
])]
|
||||||
for key in matcher_tags:
|
for key in matcher_tags:
|
||||||
processed.add(key)
|
processed.add(key)
|
||||||
if "add_icon" in matcher:
|
if "color" in matcher:
|
||||||
extra_icons += [Icon([
|
assert False
|
||||||
ShapeSpecification.from_structure(
|
if "set_main_color" in matcher:
|
||||||
x, icon_extractor, self, Color("#888888")
|
main_icon.recolor(self.get_color(matcher["set_main_color"]))
|
||||||
)
|
|
||||||
for x in matcher["add_icon"]
|
index += 1
|
||||||
])]
|
|
||||||
for key in matcher_tags:
|
|
||||||
processed.add(key)
|
|
||||||
if "color" in matcher:
|
|
||||||
assert False
|
|
||||||
if "set_main_color" in matcher:
|
|
||||||
main_icon.recolor(self.get_color(matcher["set_main_color"]))
|
|
||||||
|
|
||||||
color: Optional[Color] = None
|
color: Optional[Color] = None
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
"""
|
"""
|
||||||
OSM address tag processing.
|
OSM address tag processing.
|
||||||
|
|
||||||
Author: Sergey Vartanov (me@enzet.ru).
|
|
||||||
"""
|
"""
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Any, Dict, List
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
from colour import Color
|
from colour import Color
|
||||||
|
|
||||||
|
__author__ = "Sergey Vartanov"
|
||||||
|
__email__ = "me@enzet.ru"
|
||||||
|
|
||||||
DEFAULT_COLOR: Color = Color("#444444")
|
DEFAULT_COLOR: Color = Color("#444444")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
"""
|
"""
|
||||||
Author: Sergey Vartanov (me@enzet.ru).
|
Command-line user interface.
|
||||||
"""
|
"""
|
||||||
import argparse
|
import argparse
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
|
__author__ = "Sergey Vartanov"
|
||||||
|
__email__ = "me@enzet.ru"
|
||||||
|
|
||||||
BOXES: List[str] = [" ", "▏", "▎", "▍", "▌", "▋", "▊", "▉"]
|
BOXES: List[str] = [" ", "▏", "▎", "▍", "▌", "▋", "▊", "▉"]
|
||||||
BOXES_LENGTH: int = len(BOXES)
|
BOXES_LENGTH: int = len(BOXES)
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
"""
|
"""
|
||||||
Röntgen utility file.
|
Röntgen utility file.
|
||||||
|
|
||||||
Author: Sergey Vartanov (me@enzet.ru).
|
|
||||||
"""
|
"""
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
__author__ = "Sergey Vartanov"
|
||||||
|
__email__ = "me@enzet.ru"
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class MinMax:
|
class MinMax:
|
||||||
|
|
1424
scheme/default.yml
|
@ -1,12 +1,13 @@
|
||||||
"""
|
"""
|
||||||
Test direction processing.
|
Test direction processing.
|
||||||
|
|
||||||
Author: Sergey Vartanov (me@enzet.ru).
|
|
||||||
"""
|
"""
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from roentgen.direction import parse_vector
|
from roentgen.direction import parse_vector
|
||||||
|
|
||||||
|
__author__ = "Sergey Vartanov"
|
||||||
|
__email__ = "me@enzet.ru"
|
||||||
|
|
||||||
|
|
||||||
def test_compass_points_1() -> None:
|
def test_compass_points_1() -> None:
|
||||||
""" Test north direction. """
|
""" Test north direction. """
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
"""
|
"""
|
||||||
Test icon generation for nodes.
|
Test icon generation for nodes.
|
||||||
|
|
||||||
Author: Sergey Vartanov (me@enzet.ru).
|
|
||||||
"""
|
"""
|
||||||
from os import makedirs
|
from os import makedirs
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
@ -11,9 +9,12 @@ from roentgen.grid import draw_all_icons
|
||||||
from roentgen.icon import ShapeExtractor
|
from roentgen.icon import ShapeExtractor
|
||||||
from roentgen.scheme import Scheme
|
from roentgen.scheme import Scheme
|
||||||
|
|
||||||
SCHEME: Scheme = Scheme("scheme/default.yml")
|
__author__ = "Sergey Vartanov"
|
||||||
|
__email__ = "me@enzet.ru"
|
||||||
|
|
||||||
|
SCHEME: Scheme = Scheme(Path("scheme/default.yml"))
|
||||||
ICON_EXTRACTOR: ShapeExtractor = ShapeExtractor(
|
ICON_EXTRACTOR: ShapeExtractor = ShapeExtractor(
|
||||||
"icons/icons.svg", Path("icons/config.json")
|
Path("icons/icons.svg"), Path("icons/config.json")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
"""
|
"""
|
||||||
Test label generation for nodes.
|
Test label generation for nodes.
|
||||||
|
|
||||||
Author: Sergey Vartanov (me@enzet.ru).
|
|
||||||
"""
|
"""
|
||||||
|
from pathlib import Path
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from roentgen.scheme import Scheme
|
from roentgen.scheme import Scheme
|
||||||
from roentgen.text import Label
|
from roentgen.text import Label
|
||||||
|
|
||||||
SCHEME: Scheme = Scheme("scheme/default.yml")
|
__author__ = "Sergey Vartanov"
|
||||||
|
__email__ = "me@enzet.ru"
|
||||||
|
|
||||||
|
SCHEME: Scheme = Scheme(Path("scheme/default.yml"))
|
||||||
|
|
||||||
|
|
||||||
def construct_labels(tags) -> List[Label]:
|
def construct_labels(tags) -> List[Label]:
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
"""
|
"""
|
||||||
Test text generation.
|
Test text generation.
|
||||||
|
|
||||||
Author: Sergey Vartanov (me@enzet.ru).
|
|
||||||
"""
|
"""
|
||||||
from roentgen.text import format_voltage
|
from roentgen.text import format_voltage
|
||||||
|
|
||||||
|
__author__ = "Sergey Vartanov"
|
||||||
|
__email__ = "me@enzet.ru"
|
||||||
|
|
||||||
|
|
||||||
def test_voltage() -> None:
|
def test_voltage() -> None:
|
||||||
"""
|
"""
|
||||||
|
|