mirror of
https://github.com/enzet/map-machine.git
synced 2025-06-06 12:51:53 +02:00
Fix parsing; fix code style.
This commit is contained in:
parent
9b10f29d39
commit
7bfbf32697
9 changed files with 204 additions and 151 deletions
BIN
doc/time.png
BIN
doc/time.png
Binary file not shown.
Before Width: | Height: | Size: 416 KiB After Width: | Height: | Size: 380 KiB |
BIN
doc/trees.png
BIN
doc/trees.png
Binary file not shown.
Before Width: | Height: | Size: 171 KiB After Width: | Height: | Size: 246 KiB |
BIN
doc/user.png
BIN
doc/user.png
Binary file not shown.
Before Width: | Height: | Size: 410 KiB After Width: | Height: | Size: 376 KiB |
|
@ -1,3 +1,8 @@
|
||||||
|
"""
|
||||||
|
Construct Röntgen nodes and ways.
|
||||||
|
|
||||||
|
Author: Sergey Vartanov (me@enzet.ru).
|
||||||
|
"""
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
@ -6,9 +11,10 @@ from typing import Any, Dict, List, Optional, Set
|
||||||
|
|
||||||
from roentgen import ui
|
from roentgen import ui
|
||||||
from roentgen.extract_icon import DEFAULT_SMALL_SHAPE_ID
|
from roentgen.extract_icon import DEFAULT_SMALL_SHAPE_ID
|
||||||
from roentgen.flinger import Geo, GeoFlinger
|
from roentgen.flinger import GeoFlinger
|
||||||
from roentgen.osm_reader import OSMMember, OSMRelation, OSMWay
|
from roentgen.osm_reader import OSMMember, OSMRelation, OSMWay, OSMNode
|
||||||
from roentgen.scheme import IconSet, Scheme
|
from roentgen.scheme import IconSet, Scheme
|
||||||
|
from roentgen.util import MinMax
|
||||||
|
|
||||||
DEBUG: bool = False
|
DEBUG: bool = False
|
||||||
|
|
||||||
|
@ -40,10 +46,12 @@ class Way:
|
||||||
Way in Röntgen terms.
|
Way in Röntgen terms.
|
||||||
"""
|
"""
|
||||||
def __init__(
|
def __init__(
|
||||||
self, kind: str, nodes, path, style: Dict[str, Any],
|
self, kind: str, nodes: List[OSMNode], path, style: Dict[str, Any],
|
||||||
layer: float = 0.0, priority: float = 0, levels=None):
|
layer: float = 0.0, priority: float = 0, levels=None):
|
||||||
|
assert nodes or path
|
||||||
|
|
||||||
self.kind = kind
|
self.kind = kind
|
||||||
self.nodes = nodes
|
self.nodes: List[OSMNode] = nodes
|
||||||
self.path = path
|
self.path = path
|
||||||
self.style: Dict[str, Any] = style
|
self.style: Dict[str, Any] = style
|
||||||
self.layer = layer
|
self.layer = layer
|
||||||
|
@ -61,23 +69,20 @@ def get_float(string):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def line_center(nodes, flinger: GeoFlinger):
|
def line_center(nodes: List[OSMNode], flinger: GeoFlinger) -> np.array:
|
||||||
"""
|
"""
|
||||||
Get geometric center of nodes set.
|
Get geometric center of nodes set.
|
||||||
|
|
||||||
|
:param nodes: node list
|
||||||
|
:param flinger: flinger that remap geo positions
|
||||||
"""
|
"""
|
||||||
ma = [0, 0]
|
x, y = MinMax(), MinMax()
|
||||||
mi = [10000, 10000]
|
|
||||||
for node in nodes:
|
for node in nodes: # type: OSMNode
|
||||||
flung = flinger.fling(Geo(node.lat, node.lon))
|
flung = flinger.fling(node.position)
|
||||||
if flung[0] > ma[0]:
|
x.add(flung[0])
|
||||||
ma[0] = flung[0]
|
y.add(flung[1])
|
||||||
if flung[1] > ma[1]:
|
return np.array(((x.min_ + x.max_) / 2.0, (y.min_ + y.max_) / 2.0))
|
||||||
ma[1] = flung[1]
|
|
||||||
if flung[0] < mi[0]:
|
|
||||||
mi[0] = flung[0]
|
|
||||||
if flung[1] < mi[1]:
|
|
||||||
mi[1] = flung[1]
|
|
||||||
return [(ma[0] + mi[0]) / 2.0, (ma[1] + mi[1]) / 2.0]
|
|
||||||
|
|
||||||
|
|
||||||
def get_user_color(text: str, seed: str):
|
def get_user_color(text: str, seed: str):
|
||||||
|
@ -99,12 +104,12 @@ def get_user_color(text: str, seed: str):
|
||||||
return "#" + "0" * (6 - len(h)) + h
|
return "#" + "0" * (6 - len(h)) + h
|
||||||
|
|
||||||
|
|
||||||
def get_time_color(time: datetime):
|
def get_time_color(time: Optional[datetime]):
|
||||||
"""
|
"""
|
||||||
Generate color based on time.
|
Generate color based on time.
|
||||||
"""
|
"""
|
||||||
if not time:
|
if time is None:
|
||||||
return "#000000"
|
return "000000"
|
||||||
delta = (datetime.now() - time).total_seconds()
|
delta = (datetime.now() - time).total_seconds()
|
||||||
time_color = hex(0xFF - min(0xFF, int(delta / 500000.)))[2:]
|
time_color = hex(0xFF - min(0xFF, int(delta / 500000.)))[2:]
|
||||||
i_time_color = hex(min(0xFF, int(delta / 500000.)))[2:]
|
i_time_color = hex(min(0xFF, int(delta / 500000.)))[2:]
|
||||||
|
@ -115,11 +120,13 @@ def get_time_color(time: datetime):
|
||||||
return "#" + time_color + "AA" + i_time_color
|
return "#" + time_color + "AA" + i_time_color
|
||||||
|
|
||||||
|
|
||||||
def glue(ways: List[OSMWay]):
|
def glue(ways: List[OSMWay]) -> List[List[OSMNode]]:
|
||||||
"""
|
"""
|
||||||
Try to glue ways that share nodes.
|
Try to glue ways that share nodes.
|
||||||
|
|
||||||
|
:param ways: ways to glue
|
||||||
"""
|
"""
|
||||||
result: List[List[int]] = []
|
result: List[List[OSMNode]] = []
|
||||||
to_process: Set[OSMWay] = set()
|
to_process: Set[OSMWay] = set()
|
||||||
|
|
||||||
for way in ways: # type: OSMWay
|
for way in ways: # type: OSMWay
|
||||||
|
@ -149,17 +156,16 @@ def glue(ways: List[OSMWay]):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def get_path(nodes, shift, map_, flinger: GeoFlinger):
|
def get_path(nodes: List[OSMNode], shift: np.array, flinger: GeoFlinger) -> str:
|
||||||
"""
|
"""
|
||||||
Construct SVG path from nodes.
|
Construct SVG path from nodes.
|
||||||
"""
|
"""
|
||||||
path = ""
|
path = ""
|
||||||
prev_node = None
|
prev_node = None
|
||||||
for node_id in nodes:
|
for node in nodes:
|
||||||
node = map_.node_map[node_id]
|
flung = flinger.fling(node.position) + shift
|
||||||
flung = np.add(flinger.fling(Geo(node.lat, node.lon)), shift)
|
|
||||||
path += ("L" if prev_node else "M") + f" {flung[0]},{flung[1]} "
|
path += ("L" if prev_node else "M") + f" {flung[0]},{flung[1]} "
|
||||||
prev_node = map_.node_map[node_id]
|
prev_node = node
|
||||||
if nodes[0] == nodes[-1]:
|
if nodes[0] == nodes[-1]:
|
||||||
path += "Z"
|
path += "Z"
|
||||||
else:
|
else:
|
||||||
|
@ -198,10 +204,12 @@ class Constructor:
|
||||||
"""
|
"""
|
||||||
Way construction.
|
Way construction.
|
||||||
|
|
||||||
:param way: OSM way.
|
:param way: OSM way
|
||||||
:param tags: way tag dictionary.
|
:param tags: way tag dictionary
|
||||||
:param path: way path (if there is no nodes).
|
:param path: way path (if there is no nodes)
|
||||||
"""
|
"""
|
||||||
|
assert way or path
|
||||||
|
|
||||||
layer: float = 0
|
layer: float = 0
|
||||||
level: float = 0
|
level: float = 0
|
||||||
|
|
||||||
|
@ -209,7 +217,7 @@ class Constructor:
|
||||||
layer = get_float(tags["layer"])
|
layer = get_float(tags["layer"])
|
||||||
if "level" in tags:
|
if "level" in tags:
|
||||||
try:
|
try:
|
||||||
levels = list(map(lambda x: float(x), tags["level"].split(";")))
|
levels = list(map(float, tags["level"].split(";")))
|
||||||
level = sum(levels) / len(levels)
|
level = sum(levels) / len(levels)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
@ -221,8 +229,7 @@ class Constructor:
|
||||||
center_point = None
|
center_point = None
|
||||||
|
|
||||||
if way:
|
if way:
|
||||||
center_point = line_center(
|
center_point = line_center(way.nodes, self.flinger)
|
||||||
map(lambda x: self.map_.node_map[x], way.nodes), self.flinger)
|
|
||||||
nodes = way.nodes
|
nodes = way.nodes
|
||||||
|
|
||||||
if self.mode == "user-coloring":
|
if self.mode == "user-coloring":
|
||||||
|
@ -255,7 +262,10 @@ class Constructor:
|
||||||
if "building" in tags:
|
if "building" in tags:
|
||||||
kind = "building"
|
kind = "building"
|
||||||
if "building:levels" in tags:
|
if "building:levels" in tags:
|
||||||
|
try:
|
||||||
levels = float(tags["building:levels"])
|
levels = float(tags["building:levels"])
|
||||||
|
except ValueError:
|
||||||
|
levels = None
|
||||||
|
|
||||||
for element in self.scheme.ways: # type: Dict[str, Any]
|
for element in self.scheme.ways: # type: Dict[str, Any]
|
||||||
matched: bool = True
|
matched: bool = True
|
||||||
|
@ -286,7 +296,7 @@ class Constructor:
|
||||||
style[key] = value
|
style[key] = value
|
||||||
self.ways.append(
|
self.ways.append(
|
||||||
Way(kind, nodes, path, style, layer, 50, levels))
|
Way(kind, nodes, path, style, layer, 50, levels))
|
||||||
if center_point and way.is_cycle() or \
|
if center_point is not None and way.is_cycle() or \
|
||||||
"area" in tags and tags["area"]:
|
"area" in tags and tags["area"]:
|
||||||
icon_set: IconSet = self.scheme.get_icon(tags)
|
icon_set: IconSet = self.scheme.get_icon(tags)
|
||||||
self.nodes.append(Node(
|
self.nodes.append(Node(
|
||||||
|
@ -297,7 +307,7 @@ class Constructor:
|
||||||
style: Dict[str, Any] = {
|
style: Dict[str, Any] = {
|
||||||
"fill": "none", "stroke": "#FF0000", "stroke-width": 1}
|
"fill": "none", "stroke": "#FF0000", "stroke-width": 1}
|
||||||
self.ways.append(Way(kind, nodes, path, style, layer, 50, levels))
|
self.ways.append(Way(kind, nodes, path, style, layer, 50, levels))
|
||||||
if center_point and way.is_cycle() or \
|
if center_point is not None and way.is_cycle() or \
|
||||||
"area" in tags and tags["area"]:
|
"area" in tags and tags["area"]:
|
||||||
icon_set: IconSet = self.scheme.get_icon(tags)
|
icon_set: IconSet = self.scheme.get_icon(tags)
|
||||||
self.nodes.append(Node(
|
self.nodes.append(Node(
|
||||||
|
@ -326,12 +336,13 @@ class Constructor:
|
||||||
inners_path = glue(inners)
|
inners_path = glue(inners)
|
||||||
outers_path = glue(outers)
|
outers_path = glue(outers)
|
||||||
for nodes in outers_path:
|
for nodes in outers_path:
|
||||||
path = get_path(nodes, [0, 0], self.map_, self.flinger)
|
path = get_path(nodes, np.array([0, 0]), self.flinger)
|
||||||
p += path + " "
|
p += path + " "
|
||||||
for nodes in inners_path:
|
for nodes in inners_path:
|
||||||
nodes.reverse()
|
nodes.reverse()
|
||||||
path = get_path(nodes, [0, 0], self.map_, self.flinger)
|
path = get_path(nodes, np.array([0, 0]), self.flinger)
|
||||||
p += path + " "
|
p += path + " "
|
||||||
|
if p:
|
||||||
self.construct_way(None, tags, p)
|
self.construct_way(None, tags, p)
|
||||||
|
|
||||||
def construct_nodes(self) -> None:
|
def construct_nodes(self) -> None:
|
||||||
|
@ -345,13 +356,14 @@ class Constructor:
|
||||||
node_number: int = 0
|
node_number: int = 0
|
||||||
|
|
||||||
s = sorted(
|
s = sorted(
|
||||||
self.map_.node_map.keys(), key=lambda x: -self.map_.node_map[x].lat)
|
self.map_.node_map.keys(),
|
||||||
|
key=lambda x: -self.map_.node_map[x].position.lat)
|
||||||
|
|
||||||
for node_id in s: # type: int
|
for node_id in s: # type: int
|
||||||
node_number += 1
|
node_number += 1
|
||||||
ui.progress_bar(node_number, len(self.map_.node_map))
|
ui.progress_bar(node_number, len(self.map_.node_map))
|
||||||
node = self.map_.node_map[node_id]
|
node: OSMNode = self.map_.node_map[node_id]
|
||||||
flung = self.flinger.fling(Geo(node.lat, node.lon))
|
flung = self.flinger.fling(node.position)
|
||||||
tags = node.tags
|
tags = node.tags
|
||||||
|
|
||||||
if not self.check_level(tags):
|
if not self.check_level(tags):
|
||||||
|
@ -363,6 +375,7 @@ class Constructor:
|
||||||
if not tags:
|
if not tags:
|
||||||
continue
|
continue
|
||||||
icon_set.icons = [[DEFAULT_SMALL_SHAPE_ID]]
|
icon_set.icons = [[DEFAULT_SMALL_SHAPE_ID]]
|
||||||
|
break
|
||||||
if self.mode == "user-coloring":
|
if self.mode == "user-coloring":
|
||||||
icon_set.color = get_user_color(node.user, self.seed)
|
icon_set.color = get_user_color(node.user, self.seed)
|
||||||
if self.mode == "time":
|
if self.mode == "time":
|
||||||
|
|
|
@ -28,6 +28,8 @@ class Icon:
|
||||||
:param offset: vector that should be used to shift the path
|
:param offset: vector that should be used to shift the path
|
||||||
:param id_: shape identifier
|
:param id_: shape identifier
|
||||||
"""
|
"""
|
||||||
|
assert path
|
||||||
|
|
||||||
self.path: str = path
|
self.path: str = path
|
||||||
self.offset: np.array = offset
|
self.offset: np.array = offset
|
||||||
self.id_: str = id_
|
self.id_: str = id_
|
||||||
|
|
|
@ -95,7 +95,7 @@ class GeoFlinger:
|
||||||
|
|
||||||
self.space = space
|
self.space = space
|
||||||
|
|
||||||
def fling(self, current):
|
def fling(self, current) -> np.array:
|
||||||
"""
|
"""
|
||||||
:param current: vector to fling
|
:param current: vector to fling
|
||||||
"""
|
"""
|
||||||
|
@ -106,4 +106,4 @@ class GeoFlinger:
|
||||||
self.maximum.lat + self.minimum.lat - current.lat,
|
self.maximum.lat + self.minimum.lat - current.lat,
|
||||||
self.minimum.lat, self.maximum.lat,
|
self.minimum.lat, self.maximum.lat,
|
||||||
self.target_minimum[1], self.target_maximum[1])
|
self.target_minimum[1], self.target_maximum[1])
|
||||||
return [x, y]
|
return np.array([x, y])
|
||||||
|
|
|
@ -12,7 +12,7 @@ from svgwrite.container import Group
|
||||||
from svgwrite.path import Path
|
from svgwrite.path import Path
|
||||||
from svgwrite.shapes import Circle, Rect
|
from svgwrite.shapes import Circle, Rect
|
||||||
from svgwrite.text import Text
|
from svgwrite.text import Text
|
||||||
from typing import Dict, List
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
from roentgen import ui
|
from roentgen import ui
|
||||||
from roentgen.address import get_address
|
from roentgen.address import get_address
|
||||||
|
@ -243,10 +243,8 @@ class Painter:
|
||||||
|
|
||||||
if way.nodes:
|
if way.nodes:
|
||||||
for i in range(len(way.nodes) - 1):
|
for i in range(len(way.nodes) - 1):
|
||||||
node_1 = self.map_.node_map[way.nodes[i]]
|
flung_1 = self.flinger.fling(way.nodes[i].position)
|
||||||
node_2 = self.map_.node_map[way.nodes[i + 1]]
|
flung_2 = self.flinger.fling(way.nodes[i + 1].position)
|
||||||
flung_1 = self.flinger.fling(Geo(node_1.lat, node_1.lon))
|
|
||||||
flung_2 = self.flinger.fling(Geo(node_2.lat, node_2.lon))
|
|
||||||
|
|
||||||
self.svg.add(self.svg.path(
|
self.svg.add(self.svg.path(
|
||||||
d=("M", np.add(flung_1, shift_1), "L",
|
d=("M", np.add(flung_1, shift_1), "L",
|
||||||
|
@ -265,7 +263,7 @@ class Painter:
|
||||||
for way in ways:
|
for way in ways:
|
||||||
if way.kind == "way":
|
if way.kind == "way":
|
||||||
if way.nodes:
|
if way.nodes:
|
||||||
path = get_path(way.nodes, [0, 0], self.map_, self.flinger)
|
path = get_path(way.nodes, np.array([0, 0]), self.flinger)
|
||||||
p = Path(d=path)
|
p = Path(d=path)
|
||||||
p.update(way.style)
|
p.update(way.style)
|
||||||
self.svg.add(p)
|
self.svg.add(p)
|
||||||
|
@ -285,10 +283,8 @@ class Painter:
|
||||||
if way.levels:
|
if way.levels:
|
||||||
shift = [-5 * way.levels, 5 * way.levels]
|
shift = [-5 * way.levels, 5 * way.levels]
|
||||||
for i in range(len(way.nodes) - 1):
|
for i in range(len(way.nodes) - 1):
|
||||||
node_1 = self.map_.node_map[way.nodes[i]]
|
flung_1 = self.flinger.fling(way.nodes[i].position)
|
||||||
node_2 = self.map_.node_map[way.nodes[i + 1]]
|
flung_2 = self.flinger.fling(way.nodes[i + 1].position)
|
||||||
flung_1 = self.flinger.fling(Geo(node_1.lat, node_1.lon))
|
|
||||||
flung_2 = self.flinger.fling(Geo(node_2.lat, node_2.lon))
|
|
||||||
building_shade.add(Path(
|
building_shade.add(Path(
|
||||||
("M", flung_1, "L", flung_2, np.add(flung_2, shift),
|
("M", flung_1, "L", flung_2, np.add(flung_2, shift),
|
||||||
np.add(flung_1, shift), "Z"),
|
np.add(flung_1, shift), "Z"),
|
||||||
|
@ -310,8 +306,8 @@ class Painter:
|
||||||
if way.nodes:
|
if way.nodes:
|
||||||
shift = [0, -3]
|
shift = [0, -3]
|
||||||
if way.levels:
|
if way.levels:
|
||||||
shift = [0 * way.levels, min(-3, -1 * way.levels)]
|
shift = np.array([0 * way.levels, min(-3, -1 * way.levels)])
|
||||||
path = get_path(way.nodes, shift, self.map_, self.flinger)
|
path = get_path(way.nodes, shift, self.flinger)
|
||||||
p = Path(d=path, opacity=1)
|
p = Path(d=path, opacity=1)
|
||||||
p.update(way.style)
|
p.update(way.style)
|
||||||
self.svg.add(p)
|
self.svg.add(p)
|
||||||
|
@ -400,8 +396,7 @@ class Painter:
|
||||||
path.set_desc(title=title)
|
path.set_desc(title=title)
|
||||||
self.svg.add(path)
|
self.svg.add(path)
|
||||||
|
|
||||||
def draw_point_outline(
|
def draw_point_outline(self, icon: Icon, point, fill, mode="default"):
|
||||||
self, icon: Icon, point, fill, mode="default"):
|
|
||||||
|
|
||||||
point = np.array(list(map(lambda x: int(x), point)))
|
point = np.array(list(map(lambda x: int(x), point)))
|
||||||
|
|
||||||
|
@ -425,10 +420,12 @@ class Painter:
|
||||||
self.svg.add(path)
|
self.svg.add(path)
|
||||||
|
|
||||||
|
|
||||||
def check_level_number(tags, level):
|
def check_level_number(tags: Dict[str, Any], level: float):
|
||||||
|
"""
|
||||||
|
Check if element described by tags is no the specified level.
|
||||||
|
"""
|
||||||
if "level" in tags:
|
if "level" in tags:
|
||||||
levels = \
|
levels = map(float, tags["level"].replace(",", ".").split(";"))
|
||||||
map(lambda x: float(x), tags["level"].replace(",", ".").split(";"))
|
|
||||||
if level not in levels:
|
if level not in levels:
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
|
@ -436,19 +433,26 @@ def check_level_number(tags, level):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def check_level_overground(tags):
|
def check_level_overground(tags: Dict[str, Any]):
|
||||||
|
"""
|
||||||
|
Check if element described by tags is overground.
|
||||||
|
"""
|
||||||
if "level" in tags:
|
if "level" in tags:
|
||||||
levels = \
|
try:
|
||||||
map(lambda x: float(x), tags["level"].replace(",", ".").split(";"))
|
levels = map(float, tags["level"].replace(",", ".").split(";"))
|
||||||
for level in levels:
|
for level in levels:
|
||||||
if level <= 0:
|
if level <= 0:
|
||||||
return False
|
return False
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
if "layer" in tags:
|
if "layer" in tags:
|
||||||
levels = \
|
try:
|
||||||
map(lambda x: float(x), tags["layer"].replace(",", ".").split(";"))
|
levels = map(float, tags["layer"].replace(",", ".").split(";"))
|
||||||
for level in levels:
|
for level in levels:
|
||||||
if level <= 0:
|
if level <= 0:
|
||||||
return False
|
return False
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
if "parking" in tags and tags["parking"] == "underground":
|
if "parking" in tags and tags["parking"] == "underground":
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
"""
|
"""
|
||||||
Reading OpenStreetMap data from XML file.
|
Reading OpenStreetMap data from XML file.
|
||||||
|
|
||||||
Author: Sergey Vartanov
|
Author: Sergey Vartanov (me@enzet.ru).
|
||||||
"""
|
"""
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Dict, List, Optional
|
from typing import Dict, List, Optional, Set, Union
|
||||||
|
|
||||||
from roentgen import ui
|
from roentgen.flinger import Geo
|
||||||
|
from roentgen.ui import progress_bar
|
||||||
|
from roentgen.util import MinMax
|
||||||
|
|
||||||
|
OSM_TIME_PATTERN: str = "%Y-%m-%dT%H:%M:%SZ"
|
||||||
|
|
||||||
|
|
||||||
class OSMNode:
|
class OSMNode:
|
||||||
|
@ -15,15 +19,14 @@ class OSMNode:
|
||||||
|
|
||||||
See https://wiki.openstreetmap.org/wiki/Node
|
See https://wiki.openstreetmap.org/wiki/Node
|
||||||
"""
|
"""
|
||||||
def __init__(self, id_: int = 0, lat: float = 0, lon: float = 0):
|
def __init__(self):
|
||||||
self.id_: int = id_
|
self.id_: Optional[int] = None
|
||||||
self.lat: float = lat
|
self.position: Optional[Geo] = None
|
||||||
self.lon: float = lon
|
|
||||||
self.tags: Dict[str, str] = {}
|
self.tags: Dict[str, str] = {}
|
||||||
|
|
||||||
self.visible: Optional[str] = None
|
self.visible: Optional[str] = None
|
||||||
self.changeset: Optional[str] = None
|
self.changeset: Optional[str] = None
|
||||||
self.timestamp: Optional[str] = None
|
self.timestamp: Optional[datetime] = None
|
||||||
self.user: Optional[str] = None
|
self.user: Optional[str] = None
|
||||||
self.uid: Optional[str] = None
|
self.uid: Optional[str] = None
|
||||||
|
|
||||||
|
@ -35,13 +38,14 @@ class OSMNode:
|
||||||
:param is_full: if false, parse only ID, latitude and longitude
|
:param is_full: if false, parse only ID, latitude and longitude
|
||||||
"""
|
"""
|
||||||
self.id_ = int(get_value("id", text))
|
self.id_ = int(get_value("id", text))
|
||||||
self.lat = float(get_value("lat", text))
|
self.position = Geo(
|
||||||
self.lon = float(get_value("lon", text))
|
float(get_value("lat", text)), float(get_value("lon", text)))
|
||||||
|
|
||||||
if is_full:
|
if is_full:
|
||||||
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 = get_value("timestamp", text)
|
self.timestamp = datetime.strptime(
|
||||||
|
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)
|
||||||
|
|
||||||
|
@ -54,9 +58,9 @@ class OSMWay:
|
||||||
|
|
||||||
See https://wiki.openstreetmap.org/wiki/Way
|
See https://wiki.openstreetmap.org/wiki/Way
|
||||||
"""
|
"""
|
||||||
def __init__(self, id_: int = 0, nodes=None):
|
def __init__(self, id_: int = 0, nodes: Optional[List[OSMNode]] = None):
|
||||||
self.id_: int = id_
|
self.id_: int = id_
|
||||||
self.nodes: List[int] = [] if nodes is None else nodes
|
self.nodes: List[OSMNode] = [] if nodes is None else nodes
|
||||||
self.tags: Dict[str, str] = {}
|
self.tags: Dict[str, str] = {}
|
||||||
|
|
||||||
self.visible: Optional[str] = None
|
self.visible: Optional[str] = None
|
||||||
|
@ -78,7 +82,7 @@ class OSMWay:
|
||||||
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), "%Y-%m-%dT%H:%M:%SZ")
|
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)
|
||||||
|
|
||||||
|
@ -134,9 +138,9 @@ class OSMMember:
|
||||||
Member of OpenStreetMap relation.
|
Member of OpenStreetMap relation.
|
||||||
"""
|
"""
|
||||||
def __init__(self, text: str):
|
def __init__(self, text: str):
|
||||||
self.type_ = get_value("type", text)
|
self.type_: str = get_value("type", text)
|
||||||
self.ref = int(get_value("ref", text))
|
self.ref: int = int(get_value("ref", text))
|
||||||
self.role = get_value("role", text)
|
self.role: str = get_value("role", text)
|
||||||
|
|
||||||
|
|
||||||
def get_value(key: str, text: str):
|
def get_value(key: str, text: str):
|
||||||
|
@ -144,8 +148,9 @@ def get_value(key: str, text: str):
|
||||||
Parse xml value from the tag in the format of key="value".
|
Parse xml value from the tag in the format of key="value".
|
||||||
"""
|
"""
|
||||||
if key + '="' in text:
|
if key + '="' in text:
|
||||||
index: int = text.find(key + '="')
|
start_index: int = text.find(key + '="') + 2
|
||||||
value = text[index + len(key) + 2:text.find('"', index + len(key) + 4)]
|
end_index: int = start_index + len(key)
|
||||||
|
value = text[end_index:text.find('"', end_index + 2)]
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
@ -158,6 +163,33 @@ class Map:
|
||||||
self.way_map: Dict[int, OSMWay] = {}
|
self.way_map: Dict[int, OSMWay] = {}
|
||||||
self.relation_map: Dict[int, OSMRelation] = {}
|
self.relation_map: Dict[int, OSMRelation] = {}
|
||||||
|
|
||||||
|
self.authors: Set[str] = set()
|
||||||
|
self.time: MinMax = MinMax()
|
||||||
|
|
||||||
|
def add_node(self, node: OSMNode):
|
||||||
|
"""
|
||||||
|
Add node and update map parameters.
|
||||||
|
"""
|
||||||
|
self.node_map[node.id_] = node
|
||||||
|
if node.user:
|
||||||
|
self.authors.add(node.user)
|
||||||
|
self.time.add(node.timestamp)
|
||||||
|
|
||||||
|
def add_way(self, way: OSMWay):
|
||||||
|
"""
|
||||||
|
Add way and update map parameters.
|
||||||
|
"""
|
||||||
|
self.way_map[way.id_] = way
|
||||||
|
if way.user:
|
||||||
|
self.authors.add(way.user)
|
||||||
|
self.time.add(way.timestamp)
|
||||||
|
|
||||||
|
def add_relation(self, relation: OSMRelation):
|
||||||
|
"""
|
||||||
|
Add relation and update map parameters.
|
||||||
|
"""
|
||||||
|
self.relation_map[relation.id_] = relation
|
||||||
|
|
||||||
|
|
||||||
class OSMReader:
|
class OSMReader:
|
||||||
"""
|
"""
|
||||||
|
@ -176,75 +208,75 @@ class OSMReader:
|
||||||
lines_number: int = sum(1 for _ in open(file_name))
|
lines_number: int = sum(1 for _ in open(file_name))
|
||||||
|
|
||||||
print(f"Parsing OSM file {file_name}...")
|
print(f"Parsing OSM file {file_name}...")
|
||||||
input_file = open(file_name)
|
line_number: int = 0
|
||||||
line = input_file.readline()
|
|
||||||
line_number = 0
|
|
||||||
|
|
||||||
element = None
|
element: Optional[Union[OSMNode, OSMWay, OSMRelation]] = None
|
||||||
|
|
||||||
|
with open(file_name) as input_file:
|
||||||
|
for line in input_file.readlines(): # type: str
|
||||||
|
|
||||||
|
line = line.strip()
|
||||||
|
|
||||||
while line != "":
|
|
||||||
line_number += 1
|
line_number += 1
|
||||||
ui.progress_bar(line_number, lines_number)
|
progress_bar(line_number, lines_number)
|
||||||
|
|
||||||
# Node parsing.
|
# Node parsing.
|
||||||
|
|
||||||
if line[:6] in [" <node", "\t<node"] or line[:7] == " <node":
|
if line.startswith("<node"):
|
||||||
if not parse_nodes:
|
if not parse_nodes:
|
||||||
if parse_ways or parse_relations:
|
if parse_ways or parse_relations:
|
||||||
continue
|
continue
|
||||||
break
|
|
||||||
if line[-3] == "/":
|
|
||||||
node: OSMNode = OSMNode().parse_from_xml(line[7:-3], full)
|
|
||||||
self.map_.node_map[node.id_] = node
|
|
||||||
else:
|
else:
|
||||||
element = OSMNode().parse_from_xml(line[7:-2], full)
|
break
|
||||||
elif line in [" </node>\n", "\t</node>\n", " </node>\n"]:
|
if line[-2] == "/":
|
||||||
self.map_.node_map[element.id_] = element
|
node: OSMNode = OSMNode().parse_from_xml(line, full)
|
||||||
|
self.map_.add_node(node)
|
||||||
|
else:
|
||||||
|
element = OSMNode().parse_from_xml(line, full)
|
||||||
|
elif line == "</node>":
|
||||||
|
self.map_.add_node(element)
|
||||||
|
|
||||||
# Way parsing.
|
# Way parsing.
|
||||||
|
|
||||||
elif line[:5] in [' <way', '\t<way'] or line[:6] == " <way":
|
elif line.startswith("<way"):
|
||||||
if not parse_ways:
|
if not parse_ways:
|
||||||
if parse_relations:
|
if parse_relations:
|
||||||
continue
|
continue
|
||||||
break
|
|
||||||
if line[-3] == '/':
|
|
||||||
way = OSMWay().parse_from_xml(line[6:-3], full)
|
|
||||||
self.map_.way_map[way.id_] = way
|
|
||||||
else:
|
else:
|
||||||
element = OSMWay().parse_from_xml(line[6:-2], full)
|
break
|
||||||
elif line in [' </way>\n', '\t</way>\n'] or line == " </way>\n":
|
if line[-2] == "/":
|
||||||
self.map_.way_map[element.id_] = element
|
way = OSMWay().parse_from_xml(line, full)
|
||||||
|
self.map_.add_way(way)
|
||||||
|
else:
|
||||||
|
element = OSMWay().parse_from_xml(line, full)
|
||||||
|
elif line == "</way>":
|
||||||
|
self.map_.add_way(element)
|
||||||
|
|
||||||
# Relation parsing.
|
# Relation parsing.
|
||||||
|
|
||||||
elif line[:10] in [" <relation", "\t<relation"] or \
|
elif line.startswith("<relation"):
|
||||||
line[:11] == " <relation":
|
|
||||||
if not parse_relations:
|
if not parse_relations:
|
||||||
break
|
break
|
||||||
if line[-3] == "/":
|
if line[-2] == "/":
|
||||||
relation = OSMRelation().parse_from_xml(line[11:-3])
|
relation = OSMRelation().parse_from_xml(line)
|
||||||
self.map_.relation_map[relation.id_] = relation
|
self.map_.add_relation(relation)
|
||||||
else:
|
else:
|
||||||
element = OSMRelation().parse_from_xml(line[11:-2])
|
element = OSMRelation().parse_from_xml(line)
|
||||||
elif line in [" </relation>\n", "\t</relation>\n"] or \
|
elif line == "</relation>":
|
||||||
line == " </relation>\n":
|
self.map_.add_relation(element)
|
||||||
self.map_.relation_map[element.id_] = element
|
|
||||||
|
|
||||||
# Elements parsing.
|
# Elements parsing.
|
||||||
|
|
||||||
elif line[:6] in [" <tag", "\t\t<tag"] or line[:8] == " <tag":
|
elif line.startswith("<tag"):
|
||||||
k = get_value("k", line[7:-3])
|
k = get_value("k", line)
|
||||||
v = get_value("v", line[7:-3])
|
v = get_value("v", line)
|
||||||
element.tags[k] = v
|
element.tags[k] = v
|
||||||
elif line[:5] in [" <nd", "\t\t<nd"] or line[:7] == " <nd":
|
elif line.startswith("<nd"):
|
||||||
element.nodes.append(int(get_value("ref", line)))
|
element.nodes.append(
|
||||||
elif line[:9] in [" <member", "\t\t<member"] or \
|
self.map_.node_map[int(get_value("ref", line))])
|
||||||
line[:11] == " <member":
|
elif line.startswith("<member"):
|
||||||
element.members.append(OSMMember(line[10:-3]))
|
element.members.append(OSMMember(line))
|
||||||
line = input_file.readline()
|
|
||||||
input_file.close()
|
|
||||||
|
|
||||||
ui.progress_bar(-1, lines_number) # Complete progress bar.
|
progress_bar(-1, lines_number) # Complete progress bar.
|
||||||
|
|
||||||
return self.map_
|
return self.map_
|
||||||
|
|
|
@ -72,6 +72,8 @@ class Scheme:
|
||||||
"""
|
"""
|
||||||
if color in self.colors:
|
if color in self.colors:
|
||||||
return "#" + self.colors[color]
|
return "#" + self.colors[color]
|
||||||
|
if color.lower() in self.colors:
|
||||||
|
return "#" + self.colors[color.lower()]
|
||||||
if color.startswith("#"):
|
if color.startswith("#"):
|
||||||
return color
|
return color
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue