mirror of
https://github.com/enzet/map-machine.git
synced 2025-04-29 10:17:23 +02:00
Issue #20: fix compass points parsing; refactor.
This commit is contained in:
parent
b443a59c49
commit
6d938a1248
13 changed files with 193 additions and 166 deletions
|
@ -1,4 +1,4 @@
|
|||
language: python
|
||||
install:
|
||||
- pip install -r requirements.txt
|
||||
script: pytest -v test.py
|
||||
script: pytest -v
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
svgwrite
|
||||
numpy>=1.18.1
|
||||
PyYAML>=4.2b1
|
||||
portolan
|
||||
pyyaml>=4.2b1
|
||||
svgwrite
|
||||
urllib3>=1.25.6
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import numpy as np
|
||||
|
||||
from hashlib import sha256
|
||||
|
||||
from datetime import datetime
|
||||
from hashlib import sha256
|
||||
from typing import Any, Dict, List, Optional, Set
|
||||
|
||||
from roentgen import ui
|
||||
|
@ -30,6 +29,11 @@ class Node:
|
|||
self.layer = 0
|
||||
self.is_for_node = is_for_node
|
||||
|
||||
def get_tag(self, key: str):
|
||||
if key in self.tags:
|
||||
return self.tags[key]
|
||||
return None
|
||||
|
||||
|
||||
class Way:
|
||||
"""
|
||||
|
@ -95,13 +99,12 @@ def get_user_color(text: str, seed: str):
|
|||
return "#" + "0" * (6 - len(h)) + h
|
||||
|
||||
|
||||
def get_time_color(time):
|
||||
def get_time_color(time: datetime):
|
||||
"""
|
||||
Generate color based on time.
|
||||
"""
|
||||
if not time:
|
||||
return "#000000"
|
||||
time = datetime.strptime(time, "%Y-%m-%dT%H:%M:%SZ")
|
||||
delta = (datetime.now() - time).total_seconds()
|
||||
time_color = hex(0xFF - min(0xFF, int(delta / 500000.)))[2:]
|
||||
i_time_color = hex(min(0xFF, int(delta / 500000.)))[2:]
|
||||
|
|
|
@ -3,42 +3,51 @@ Direction tag support.
|
|||
|
||||
Author: Sergey Vartanov (me@enzet.ru).
|
||||
"""
|
||||
import math
|
||||
from typing import Iterator, List, Optional, Union
|
||||
|
||||
import numpy as np
|
||||
|
||||
from typing import Dict, List, Optional, Iterator
|
||||
|
||||
|
||||
DIRECTIONS: Dict[str, np.array] = {
|
||||
"N": np.array((0, -1)),
|
||||
"E": np.array((1, 0)),
|
||||
"W": np.array((-1, 0)),
|
||||
"S": np.array((0, 1)),
|
||||
}
|
||||
from portolan import middle
|
||||
|
||||
|
||||
def parse_vector(text: str) -> np.array:
|
||||
"""
|
||||
Parse vector from text representation: letters N, E, W, S or 360-degree
|
||||
notation. E.g. NW, 270.
|
||||
Parse vector from text representation: compass points or 360-degree
|
||||
notation. E.g. "NW", "270".
|
||||
|
||||
:param text: vector text representation
|
||||
:return: parsed normalized vector
|
||||
"""
|
||||
def degree_to_radian(degree: float):
|
||||
""" Convert value in degrees to radians. """
|
||||
return degree / 180 * np.pi - np.pi / 2
|
||||
|
||||
try:
|
||||
degree: float = float(text) / 180 * math.pi - math.pi / 2
|
||||
return np.array((math.cos(degree), math.sin(degree)))
|
||||
except ValueError as e:
|
||||
vector: np.array = np.array((0, 0))
|
||||
for char in text: # type: str
|
||||
if char not in DIRECTIONS:
|
||||
return None
|
||||
vector += DIRECTIONS[char]
|
||||
return vector / np.linalg.norm(vector)
|
||||
radians: float = degree_to_radian(float(text))
|
||||
return np.array((np.cos(radians), np.sin(radians)))
|
||||
except ValueError:
|
||||
radians: float = degree_to_radian(middle(text))
|
||||
return np.array((np.cos(radians), np.sin(radians)))
|
||||
|
||||
|
||||
def rotation_matrix(angle):
|
||||
"""
|
||||
Get a matrix to rotate 2D vector by the angle.
|
||||
|
||||
:param angle: angle in radians
|
||||
"""
|
||||
return np.array([
|
||||
[np.cos(angle), np.sin(angle)],
|
||||
[-np.sin(angle), np.cos(angle)]])
|
||||
|
||||
|
||||
class Sector:
|
||||
"""
|
||||
Sector described by two vectors.
|
||||
"""
|
||||
def __init__(self, text: str):
|
||||
"""
|
||||
:param text: sector text representation. E.g. "70-210", "N-NW"
|
||||
"""
|
||||
self.start: Optional[np.array]
|
||||
self.end: Optional[np.array]
|
||||
|
||||
|
@ -47,16 +56,23 @@ class Sector:
|
|||
self.start = parse_vector(parts[0])
|
||||
self.end = parse_vector(parts[1])
|
||||
else:
|
||||
self.start = parse_vector(text)
|
||||
self.end = None
|
||||
vector = parse_vector(text)
|
||||
angle = np.pi / 12
|
||||
self.start = np.dot(rotation_matrix(angle), vector)
|
||||
self.end = np.dot(rotation_matrix(-angle), vector)
|
||||
|
||||
def draw(self, center: np.array, radius: float) -> List:
|
||||
def draw(self, center: np.array, radius: float) \
|
||||
-> Optional[List[Union[float, str, np.array]]]:
|
||||
"""
|
||||
Construct SVG "d" for arc element.
|
||||
Construct SVG path commands for arc element.
|
||||
|
||||
:param center: arc center
|
||||
:param center: arc center point
|
||||
:param radius: arc radius
|
||||
:return: SVG path commands
|
||||
"""
|
||||
if self.start is None or self.end is None:
|
||||
return None
|
||||
|
||||
start: np.array = center + radius * self.end
|
||||
end: np.array = center + radius * self.start
|
||||
|
||||
|
@ -79,5 +95,14 @@ class DirectionSet:
|
|||
def __str__(self):
|
||||
return ", ".join(map(str, self.sectors))
|
||||
|
||||
def draw(self, center: np.array, radius: float) -> Iterator[str]:
|
||||
return map(lambda x: x.draw(center, radius), self.sectors)
|
||||
def draw(self, center: np.array, radius: float) -> Iterator[List]:
|
||||
"""
|
||||
Construct SVG "d" for arc elements.
|
||||
|
||||
:param center: center point of all arcs
|
||||
:param radius: radius of all arcs
|
||||
:return: list of "d" values
|
||||
"""
|
||||
return filter(
|
||||
lambda x: x is not None,
|
||||
map(lambda x: x.draw(center, radius), self.sectors))
|
||||
|
|
|
@ -5,6 +5,8 @@ import math
|
|||
import numpy as np
|
||||
from typing import Optional
|
||||
|
||||
from roentgen.util import MinMax
|
||||
|
||||
|
||||
def get_ratio(maximum, minimum, ratio: float = 1):
|
||||
return (maximum[0] - minimum[0]) * ratio / (maximum[1] - minimum[1])
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
"""
|
||||
Icon grid drawing.
|
||||
|
||||
Author: Sergey Vartanov (me@enzet.ru).
|
||||
"""
|
||||
import numpy as np
|
||||
import os
|
||||
import random
|
||||
import svgwrite
|
||||
from svgwrite import Drawing
|
||||
import yaml
|
||||
|
||||
from roentgen.extract_icon import Icon, IconExtractor
|
||||
|
@ -12,32 +12,21 @@ from roentgen.extract_icon import Icon, IconExtractor
|
|||
from typing import List
|
||||
|
||||
|
||||
def draw_grid():
|
||||
def draw_grid(step: float = 24, columns: int = 16):
|
||||
"""
|
||||
Draw all possible icon combinations in grid.
|
||||
|
||||
:param step: horizontal and vertical distance between icons
|
||||
:param columns: the number of columns in grid
|
||||
"""
|
||||
tags_file_name = "data/tags.yml"
|
||||
|
||||
scheme = yaml.load(open(tags_file_name), Loader=yaml.FullLoader)
|
||||
|
||||
icons_file_name = "icons/icons.svg"
|
||||
icon_grid_file_name = "icon_grid.svg"
|
||||
icon_colors_file_name = "data/icon_colors"
|
||||
|
||||
icon_colors = [("FFFFFF", "444444")]
|
||||
|
||||
if os.path.isfile(icon_colors_file_name):
|
||||
icon_colors_file = open(icon_colors_file_name)
|
||||
for line in icon_colors_file.read().split("\n"):
|
||||
background_color = \
|
||||
hex(int(line[0:3]))[2:] + hex(int(line[3:6]))[2:] + \
|
||||
hex(int(line[6:9]))[2:]
|
||||
foreground_color = \
|
||||
hex(int(line[10:13]))[2:] + hex(int(line[13:16]))[2:] + \
|
||||
hex(int(line[16:19]))[2:]
|
||||
icon_colors.append((background_color, foreground_color))
|
||||
|
||||
step: float = 24
|
||||
|
||||
width: float = 24 * 16
|
||||
|
||||
width: float = step * columns
|
||||
point: np.array = np.array((step / 2, step / 2))
|
||||
|
||||
to_draw = []
|
||||
|
@ -89,20 +78,20 @@ def draw_grid():
|
|||
icons.append(icon_set)
|
||||
number += 1
|
||||
|
||||
height = int(number / (width / step) + 1) * step
|
||||
height: int = int(int(number / (width / step) + 1) * step)
|
||||
|
||||
svg = svgwrite.Drawing(icon_grid_file_name, (width, height))
|
||||
svg: Drawing = Drawing(icon_grid_file_name, (width, height))
|
||||
|
||||
svg.add(svg.rect((0, 0), (width, height), fill="#FFFFFF"))
|
||||
|
||||
for icon in icons:
|
||||
background_color, foreground_color = random.choice(icon_colors)
|
||||
background_color, foreground_color = "#FFFFFF", "#444444"
|
||||
svg.add(svg.rect(
|
||||
point - np.array((-10, -10)), (20, 20),
|
||||
fill=f"#{background_color}"))
|
||||
fill=background_color))
|
||||
for i in icon: # type: Icon
|
||||
path = i.get_path(svg, point)
|
||||
path.update({"fill": f"#{foreground_color}"})
|
||||
path.update({"fill": foreground_color})
|
||||
svg.add(path)
|
||||
point += np.array((step, 0))
|
||||
if point[0] > width - 8:
|
||||
|
@ -112,4 +101,5 @@ def draw_grid():
|
|||
|
||||
print(f"Icons: {number}.")
|
||||
|
||||
svg.write(open(icon_grid_file_name, "w"))
|
||||
with open(icon_grid_file_name, "w") as output_file:
|
||||
svg.write(output_file)
|
||||
|
|
|
@ -15,6 +15,7 @@ from svgwrite.text import Text
|
|||
from typing import Dict, List
|
||||
|
||||
from roentgen import ui
|
||||
from roentgen.address import get_address
|
||||
from roentgen.constructor import Constructor, get_path, Node, Way
|
||||
from roentgen.flinger import GeoFlinger, Geo
|
||||
from roentgen.grid import draw_grid
|
||||
|
@ -39,7 +40,7 @@ class Painter:
|
|||
"""
|
||||
def __init__(
|
||||
self, show_missing_tags: bool, overlap: int, draw_nodes: bool,
|
||||
mode: str, draw_captions: bool, map_: Map, flinger: GeoFlinger,
|
||||
mode: str, draw_captions: str, map_: Map, flinger: GeoFlinger,
|
||||
svg: svgwrite.Drawing, icon_extractor: IconExtractor,
|
||||
scheme: Scheme):
|
||||
|
||||
|
@ -47,7 +48,7 @@ class Painter:
|
|||
self.overlap: int = overlap
|
||||
self.draw_nodes: bool = draw_nodes
|
||||
self.mode: str = mode
|
||||
self.draw_captions = draw_captions
|
||||
self.draw_captions: str = draw_captions
|
||||
|
||||
self.map_: Map = map_
|
||||
self.flinger: GeoFlinger = flinger
|
||||
|
@ -150,7 +151,6 @@ class Painter:
|
|||
Construct labels for not processed tags.
|
||||
"""
|
||||
texts = []
|
||||
address: List[str] = []
|
||||
name = None
|
||||
alt_name = None
|
||||
if "name" in tags:
|
||||
|
@ -179,24 +179,9 @@ class Painter:
|
|||
else:
|
||||
alt_name = ""
|
||||
alt_name += "бывш. " + tags["old_name"]
|
||||
if "addr:postcode" in tags and self.draw_captions != "main":
|
||||
address.append(tags["addr:postcode"])
|
||||
tags.pop("addr:postcode", None)
|
||||
if "addr:country" in tags and self.draw_captions != "main":
|
||||
address.append(tags["addr:country"])
|
||||
tags.pop("addr:country", None)
|
||||
if "addr:city" in tags and self.draw_captions != "main":
|
||||
address.append(tags["addr:city"])
|
||||
tags.pop("addr:city", None)
|
||||
if "addr:street" in tags and self.draw_captions != "main":
|
||||
street = tags["addr:street"]
|
||||
if street.startswith("улица "):
|
||||
street = "ул. " + street[len("улица "):]
|
||||
address.append(street)
|
||||
tags.pop("addr:street", None)
|
||||
if "addr:housenumber" in tags:
|
||||
address.append(tags["addr:housenumber"])
|
||||
tags.pop("addr:housenumber", None)
|
||||
|
||||
address = get_address(tags, self.draw_captions)
|
||||
|
||||
if name:
|
||||
texts.append({"text": name, "fill": "#000000"})
|
||||
if alt_name:
|
||||
|
@ -338,8 +323,7 @@ class Painter:
|
|||
# Trees
|
||||
|
||||
for node in nodes:
|
||||
if not("natural" in node.tags and
|
||||
node.tags["natural"] == "tree" and
|
||||
if not(node.get_tag("natural") == "tree" and
|
||||
"diameter_crown" in node.tags):
|
||||
continue
|
||||
self.svg.add(Circle(
|
||||
|
@ -348,23 +332,27 @@ class Painter:
|
|||
|
||||
# Directions
|
||||
|
||||
for node in nodes:
|
||||
if not ("tourism" in node.tags and
|
||||
node.tags["tourism"] == "viewpoint" and
|
||||
"direction" in node.tags):
|
||||
for node in nodes: # type: Node
|
||||
direction = None
|
||||
if node.get_tag("tourism") == "viewpoint":
|
||||
direction = node.get_tag("direction")
|
||||
if node.get_tag("man_made") == "surveillance":
|
||||
direction = node.get_tag("camera:direction")
|
||||
|
||||
if not direction:
|
||||
continue
|
||||
|
||||
DIRECTION_RADIUS: int = 50
|
||||
DIRECTION_RADIUS: int = 25
|
||||
DIRECTION_COLOR: str = self.scheme.get_color("direction_color")
|
||||
|
||||
for path in DirectionSet(node.tags["direction"])\
|
||||
for path in DirectionSet(direction)\
|
||||
.draw(node.point, DIRECTION_RADIUS):
|
||||
gradient = self.svg.defs.add(self.svg.radialGradient(
|
||||
center=node.point, r=DIRECTION_RADIUS,
|
||||
gradientUnits="userSpaceOnUse"))
|
||||
gradient\
|
||||
.add_stop_color(0, DIRECTION_COLOR, opacity=0)\
|
||||
.add_stop_color(1, DIRECTION_COLOR, opacity=0.7)
|
||||
.add_stop_color(1, DIRECTION_COLOR, opacity=0.4)
|
||||
self.svg.add(self.svg.path(
|
||||
d=["M", node.point] + path + ["L", node.point, "Z"],
|
||||
fill=gradient.get_paint_server()))
|
||||
|
@ -372,12 +360,14 @@ class Painter:
|
|||
# All other nodes
|
||||
|
||||
nodes = sorted(nodes, key=lambda x: x.layer)
|
||||
for node in nodes: # type: Node
|
||||
for index, node in enumerate(nodes): # type: int, Node
|
||||
if "natural" in node.tags and \
|
||||
node.tags["natural"] == "tree" and \
|
||||
"diameter_crown" in node.tags:
|
||||
continue
|
||||
ui.progress_bar(index, len(nodes), step=10)
|
||||
self.draw_shapes(node, points)
|
||||
ui.progress_bar(-1, len(nodes), step=10)
|
||||
|
||||
if self.draw_captions == "no":
|
||||
return
|
||||
|
@ -405,7 +395,7 @@ class Painter:
|
|||
point = np.array(list(map(lambda x: int(x), point)))
|
||||
title: str = "\n".join(map(lambda x: x + ": " + tags[x], tags))
|
||||
|
||||
path = icon.get_path(self.svg, point)
|
||||
path: svgwrite.path.Path = icon.get_path(self.svg, point)
|
||||
path.update({"fill": fill})
|
||||
path.set_desc(title=title)
|
||||
self.svg.add(path)
|
||||
|
@ -518,15 +508,14 @@ def main():
|
|||
min1 = Geo(boundary_box[1], boundary_box[0])
|
||||
max1 = Geo(boundary_box[3], boundary_box[2])
|
||||
|
||||
authors = {}
|
||||
missing_tags = {}
|
||||
points = []
|
||||
|
||||
scheme = Scheme(TAGS_FILE_NAME, COLORS_FILE_NAME)
|
||||
scheme: Scheme = Scheme(TAGS_FILE_NAME, COLORS_FILE_NAME)
|
||||
|
||||
flinger = GeoFlinger(min1, max1, [0, 0], [w, h])
|
||||
flinger: GeoFlinger = GeoFlinger(min1, max1, [0, 0], [w, h])
|
||||
|
||||
icon_extractor = IconExtractor(ICONS_FILE_NAME)
|
||||
icon_extractor: IconExtractor = IconExtractor(ICONS_FILE_NAME)
|
||||
|
||||
def check_level(x):
|
||||
""" Draw objects on all levels. """
|
||||
|
@ -544,14 +533,14 @@ def main():
|
|||
""" Draw objects on the specified level. """
|
||||
return not check_level_number(x, float(options.level))
|
||||
|
||||
constructor = Constructor(
|
||||
constructor: Constructor = Constructor(
|
||||
check_level, options.mode, options.seed, map_, flinger, scheme)
|
||||
if options.draw_ways:
|
||||
constructor.construct_ways()
|
||||
constructor.construct_relations()
|
||||
constructor.construct_nodes()
|
||||
|
||||
painter = Painter(
|
||||
painter: Painter = Painter(
|
||||
show_missing_tags=options.show_missing_tags, overlap=options.overlap,
|
||||
draw_nodes=options.draw_nodes, mode=options.mode,
|
||||
draw_captions=options.draw_captions,
|
||||
|
@ -569,57 +558,11 @@ def main():
|
|||
(w - flinger.space[0], 0), (flinger.space[0], h), fill="#FFFFFF"))
|
||||
|
||||
if options.show_index:
|
||||
print(min1.lon, max1.lon)
|
||||
print(min1.lat, max1.lat)
|
||||
|
||||
lon_step = 0.001
|
||||
lat_step = 0.001
|
||||
|
||||
matrix = []
|
||||
|
||||
lat_number = int((max1.lat - min1.lat) / lat_step) + 1
|
||||
lon_number = int((max1.lon - min1.lon) / lon_step) + 1
|
||||
|
||||
for i in range(lat_number):
|
||||
row = []
|
||||
for j in range(lon_number):
|
||||
row.append(0)
|
||||
matrix.append(row)
|
||||
|
||||
for node_id in map_.node_map:
|
||||
node = map_.node_map[node_id]
|
||||
i = int((node.lat - min1.lat) / lat_step)
|
||||
j = int((node.lon - min1.lon) / lon_step)
|
||||
if (0 <= i < lat_number) and (0 <= j < lon_number):
|
||||
matrix[i][j] += 1
|
||||
if "tags" in node:
|
||||
matrix[i][j] += len(node.tags)
|
||||
|
||||
for way_id in map_.way_map:
|
||||
way = map_.way_map[way_id]
|
||||
if "tags" in way:
|
||||
for node_id in way.nodes:
|
||||
node = map_.node_map[node_id]
|
||||
i = int((node.lat - min1.lat) / lat_step)
|
||||
j = int((node.lon - min1.lon) / lon_step)
|
||||
if (0 <= i < lat_number) and (0 <= j < lon_number):
|
||||
matrix[i][j] += len(way.tags) / float(
|
||||
len(way.nodes))
|
||||
|
||||
for i in range(lat_number):
|
||||
for j in range(lon_number):
|
||||
t1 = flinger.fling(Geo(
|
||||
min1.lat + i * lat_step, min1.lon + j * lon_step))
|
||||
t2 = flinger.fling(Geo(
|
||||
min1.lat + (i + 1) * lat_step,
|
||||
min1.lon + (j + 1) * lon_step))
|
||||
svg.add(Text(
|
||||
str(int(matrix[i][j])),
|
||||
(((t1 + t2) * 0.5)[0], ((t1 + t2) * 0.5)[1] + 40),
|
||||
font_size=80, fill="440000",
|
||||
opacity=0.1, align="center"))
|
||||
draw_index(flinger, map_, max1, min1, svg)
|
||||
|
||||
print("Writing output SVG...")
|
||||
svg.write(open(options.output_file_name, "w"))
|
||||
print("Done")
|
||||
|
||||
top_missing_tags = \
|
||||
sorted(missing_tags.keys(), key=lambda x: -missing_tags[x])
|
||||
|
@ -629,6 +572,47 @@ def main():
|
|||
f'- {{tag: "{tag}", count: {missing_tags[tag]}}}\n')
|
||||
missing_tags_file.close()
|
||||
|
||||
top_authors = sorted(authors.keys(), key=lambda x: -authors[x])
|
||||
for author in top_authors:
|
||||
print(f"{author}: {authors[author]}")
|
||||
|
||||
def draw_index(flinger, map_, max1, min1, svg):
|
||||
print(min1.lon, max1.lon)
|
||||
print(min1.lat, max1.lat)
|
||||
lon_step = 0.001
|
||||
lat_step = 0.001
|
||||
matrix = []
|
||||
lat_number = int((max1.lat - min1.lat) / lat_step) + 1
|
||||
lon_number = int((max1.lon - min1.lon) / lon_step) + 1
|
||||
for i in range(lat_number):
|
||||
row = []
|
||||
for j in range(lon_number):
|
||||
row.append(0)
|
||||
matrix.append(row)
|
||||
for node_id in map_.node_map: # type: int
|
||||
node = map_.node_map[node_id]
|
||||
i = int((node.lat - min1.lat) / lat_step)
|
||||
j = int((node.lon - min1.lon) / lon_step)
|
||||
if (0 <= i < lat_number) and (0 <= j < lon_number):
|
||||
matrix[i][j] += 1
|
||||
if "tags" in node:
|
||||
matrix[i][j] += len(node.tags)
|
||||
for way_id in map_.way_map: # type: int
|
||||
way = map_.way_map[way_id]
|
||||
if "tags" in way:
|
||||
for node_id in way.nodes:
|
||||
node = map_.node_map[node_id]
|
||||
i = int((node.lat - min1.lat) / lat_step)
|
||||
j = int((node.lon - min1.lon) / lon_step)
|
||||
if (0 <= i < lat_number) and (0 <= j < lon_number):
|
||||
matrix[i][j] += len(way.tags) / float(
|
||||
len(way.nodes))
|
||||
for i in range(lat_number):
|
||||
for j in range(lon_number):
|
||||
t1 = flinger.fling(Geo(
|
||||
min1.lat + i * lat_step, min1.lon + j * lon_step))
|
||||
t2 = flinger.fling(Geo(
|
||||
min1.lat + (i + 1) * lat_step,
|
||||
min1.lon + (j + 1) * lon_step))
|
||||
svg.add(Text(
|
||||
str(int(matrix[i][j])),
|
||||
(((t1 + t2) * 0.5)[0], ((t1 + t2) * 0.5)[1] + 40),
|
||||
font_size=80, fill="440000",
|
||||
opacity=0.1, align="center"))
|
||||
|
|
|
@ -3,6 +3,7 @@ Reading OpenStreetMap data from XML file.
|
|||
|
||||
Author: Sergey Vartanov
|
||||
"""
|
||||
from datetime import datetime
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
from roentgen import ui
|
||||
|
@ -61,7 +62,7 @@ class OSMWay:
|
|||
self.visible: Optional[str] = None
|
||||
self.changeset: Optional[str] = None
|
||||
self.user: Optional[str] = None
|
||||
self.timestamp: Optional[str] = None
|
||||
self.timestamp: Optional[datetime] = None
|
||||
self.uid: Optional[str] = None
|
||||
|
||||
def parse_from_xml(self, text: str, is_full: bool = False) -> "OSMWay":
|
||||
|
@ -76,7 +77,8 @@ class OSMWay:
|
|||
if is_full:
|
||||
self.visible = get_value("visible", text)
|
||||
self.changeset = get_value("changeset", text)
|
||||
self.timestamp = get_value("timestamp", text)
|
||||
self.timestamp = datetime.strptime(
|
||||
get_value("timestamp", text), "%Y-%m-%dT%H:%M:%SZ")
|
||||
self.user = get_value("user", text)
|
||||
self.uid = get_value("uid", text)
|
||||
|
||||
|
@ -190,8 +192,7 @@ class OSMReader:
|
|||
if not parse_nodes:
|
||||
if parse_ways or parse_relations:
|
||||
continue
|
||||
else:
|
||||
break
|
||||
break
|
||||
if line[-3] == "/":
|
||||
node: OSMNode = OSMNode().parse_from_xml(line[7:-3], full)
|
||||
self.map_.node_map[node.id_] = node
|
||||
|
@ -206,8 +207,7 @@ class OSMReader:
|
|||
if not parse_ways:
|
||||
if parse_relations:
|
||||
continue
|
||||
else:
|
||||
break
|
||||
break
|
||||
if line[-3] == '/':
|
||||
way = OSMWay().parse_from_xml(line[6:-3], full)
|
||||
self.map_.way_map[way.id_] = way
|
||||
|
|
|
@ -39,7 +39,8 @@ def parse_options(args):
|
|||
"-s", "--size",
|
||||
metavar="<width>,<height>",
|
||||
help="output SVG file size in pixels",
|
||||
dest="size")
|
||||
dest="size",
|
||||
required=True)
|
||||
parser.add_argument(
|
||||
"-nn", "--no-draw-nodes",
|
||||
dest="draw_nodes",
|
||||
|
|
|
@ -5,4 +5,4 @@ class MinMax:
|
|||
|
||||
def add(self, value):
|
||||
self.min_ = value if not self.min_ or value < self.min_ else self.min_
|
||||
self.max_ = value if not self.max_ or value > self.max_ else self.max_
|
||||
self.max_ = value if not self.max_ or value > self.max_ else self.max_
|
||||
|
|
22
test/test_direction.py
Normal file
22
test/test_direction.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
"""
|
||||
Test direction processing.
|
||||
|
||||
Author: Sergey Vartanov (me@enzet.ru).
|
||||
"""
|
||||
import numpy as np
|
||||
|
||||
from roentgen.direction import parse_vector
|
||||
|
||||
|
||||
def test_compass_points_1():
|
||||
assert np.allclose(parse_vector("N"), np.array([0, -1]))
|
||||
|
||||
|
||||
def test_compass_points_2():
|
||||
root: np.float64 = -np.sqrt(2) / 2
|
||||
assert np.allclose(parse_vector("NW"), np.array([root, root]))
|
||||
|
||||
|
||||
def test_compass_points_3():
|
||||
assert np.allclose(
|
||||
parse_vector("SSW"), np.array([-0.38268343, 0.92387953]))
|
|
@ -2,10 +2,6 @@
|
|||
Author: Sergey Vartanov (me@enzet.ru).
|
||||
"""
|
||||
|
||||
import os
|
||||
import random
|
||||
import yaml
|
||||
|
||||
from roentgen.flinger import map_
|
||||
from roentgen.grid import draw_grid
|
||||
|
3
test_main.py
Normal file
3
test_main.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
def test_main():
|
||||
assert True
|
||||
|
Loading…
Add table
Reference in a new issue