mirror of
https://github.com/enzet/map-machine.git
synced 2025-05-31 01:46:26 +02:00
Fix inner path and outer path drawing. Make flinger more simple.
This commit is contained in:
parent
8d8080181e
commit
f6a15e7a71
10 changed files with 478 additions and 410 deletions
|
@ -11,7 +11,7 @@ from typing import Any, Dict, List, Optional, Set
|
|||
|
||||
from roentgen import ui
|
||||
from roentgen.extract_icon import DEFAULT_SMALL_SHAPE_ID
|
||||
from roentgen.flinger import GeoFlinger
|
||||
from roentgen.flinger import Flinger
|
||||
from roentgen.osm_reader import OSMMember, OSMRelation, OSMWay, OSMNode
|
||||
from roentgen.scheme import IconSet, Scheme
|
||||
from roentgen.util import MinMax
|
||||
|
@ -19,20 +19,47 @@ from roentgen.util import MinMax
|
|||
DEBUG: bool = False
|
||||
|
||||
|
||||
def is_clockwise(polygon: List[OSMNode]) -> bool:
|
||||
"""
|
||||
Are polygon nodes are in clockwise order.
|
||||
"""
|
||||
count: float = 0
|
||||
for index in range(len(polygon)): # type: int
|
||||
next_index: int = 0 if index == len(polygon) - 1 else index + 1
|
||||
count += (
|
||||
(polygon[next_index].position[0] - polygon[index].position[0]) *
|
||||
(polygon[next_index].position[1] + polygon[index].position[1]))
|
||||
return count >= 0
|
||||
|
||||
|
||||
def make_clockwise(polygon: List[OSMNode]) -> List[OSMNode]:
|
||||
if is_clockwise(polygon):
|
||||
return polygon
|
||||
else:
|
||||
return list(reversed(polygon))
|
||||
|
||||
|
||||
def make_counter_clockwise(polygon: List[OSMNode]) -> List[OSMNode]:
|
||||
if not is_clockwise(polygon):
|
||||
return polygon
|
||||
else:
|
||||
return list(reversed(polygon))
|
||||
|
||||
|
||||
class Node:
|
||||
"""
|
||||
Node in Röntgen terms.
|
||||
"""
|
||||
def __init__(
|
||||
self, icon_set: IconSet, tags: Dict[str, str],
|
||||
point: (float, float), path: Optional[str],
|
||||
point: np.array, coordinates: np.array,
|
||||
priority: int = 0, is_for_node: bool = True):
|
||||
assert point is not None
|
||||
|
||||
self.icon_set: IconSet = icon_set
|
||||
self.tags = tags
|
||||
self.point = point
|
||||
self.path = path
|
||||
self.point: np.array = point
|
||||
self.coordinates: np.array = coordinates
|
||||
self.priority = priority
|
||||
self.layer = 0
|
||||
self.is_for_node = is_for_node
|
||||
|
@ -48,43 +75,50 @@ class Way:
|
|||
Way in Röntgen terms.
|
||||
"""
|
||||
def __init__(
|
||||
self, kind: str, nodes: List[OSMNode], path, style: Dict[str, Any],
|
||||
layer: float = 0.0, priority: float = 0, levels=None):
|
||||
assert nodes or path
|
||||
|
||||
self, kind: str, inners, outers, style: Dict[str, Any],
|
||||
layer: float = 0.0, levels=None):
|
||||
self.kind = kind
|
||||
self.nodes: List[OSMNode] = nodes
|
||||
self.path = path
|
||||
self.inners = inners
|
||||
self.outers = outers
|
||||
self.style: Dict[str, Any] = style
|
||||
self.layer = layer
|
||||
self.priority = priority
|
||||
self.levels = levels
|
||||
|
||||
def get_path(
|
||||
self, flinger: Flinger, shift: np.array = np.array((0, 0))) -> str:
|
||||
"""
|
||||
Get SVG path commands.
|
||||
|
||||
def get_float(string):
|
||||
"""
|
||||
Try to parse float from a string.
|
||||
"""
|
||||
try:
|
||||
return float(string)
|
||||
except ValueError:
|
||||
return 0
|
||||
:param shift: shift vector
|
||||
"""
|
||||
path: str = ""
|
||||
|
||||
for outer_nodes in self.outers:
|
||||
path += get_path(
|
||||
make_counter_clockwise(outer_nodes), shift, flinger) + " "
|
||||
|
||||
for inner_nodes in self.inners:
|
||||
path += get_path(
|
||||
make_clockwise(inner_nodes), shift, flinger) + " "
|
||||
|
||||
return path
|
||||
|
||||
|
||||
def line_center(nodes: List[OSMNode], flinger: GeoFlinger) -> np.array:
|
||||
def line_center(nodes: List[OSMNode], flinger: Flinger) -> np.array:
|
||||
"""
|
||||
Get geometric center of nodes set.
|
||||
|
||||
:param nodes: node list
|
||||
:param flinger: flinger that remap geo positions
|
||||
"""
|
||||
x, y = MinMax(), MinMax()
|
||||
boundary = [MinMax(), MinMax()]
|
||||
|
||||
for node in nodes: # type: OSMNode
|
||||
flung = flinger.fling(node.position)
|
||||
x.update(flung[0])
|
||||
y.update(flung[1])
|
||||
return np.array(((x.min_ + x.max_) / 2.0, (y.min_ + y.max_) / 2.0))
|
||||
boundary[0].update(node.position[0])
|
||||
boundary[1].update(node.position[1])
|
||||
center_coordinates = np.array((boundary[0].center(), boundary[1].center()))
|
||||
|
||||
return flinger.fling(center_coordinates), center_coordinates
|
||||
|
||||
|
||||
def get_user_color(text: str, seed: str):
|
||||
|
@ -158,7 +192,7 @@ def glue(ways: List[OSMWay]) -> List[List[OSMNode]]:
|
|||
return result
|
||||
|
||||
|
||||
def get_path(nodes: List[OSMNode], shift: np.array, flinger: GeoFlinger) -> str:
|
||||
def get_path(nodes: List[OSMNode], shift: np.array, flinger: Flinger) -> str:
|
||||
"""
|
||||
Construct SVG path from nodes.
|
||||
"""
|
||||
|
@ -179,12 +213,15 @@ class Constructor:
|
|||
"""
|
||||
Röntgen node and way constructor.
|
||||
"""
|
||||
def __init__(self, check_level, mode, seed, map_, flinger, scheme: Scheme):
|
||||
def __init__(
|
||||
self, check_level, mode, seed, map_, flinger: Flinger,
|
||||
scheme: Scheme):
|
||||
|
||||
self.check_level = check_level
|
||||
self.mode = mode
|
||||
self.seed = seed
|
||||
self.map_ = map_
|
||||
self.flinger = flinger
|
||||
self.flinger: Flinger = flinger
|
||||
self.scheme: Scheme = scheme
|
||||
|
||||
self.nodes: List[Node] = []
|
||||
|
@ -194,44 +231,49 @@ class Constructor:
|
|||
"""
|
||||
Construct Röntgen ways.
|
||||
"""
|
||||
way_number: int = 0
|
||||
for way_id in self.map_.way_map: # type: int
|
||||
ui.progress_bar(
|
||||
way_number, len(self.map_.way_map),
|
||||
text="Constructing ways")
|
||||
way_number += 1
|
||||
way: OSMWay = self.map_.way_map[way_id]
|
||||
if not self.check_level(way.tags):
|
||||
continue
|
||||
self.construct_way(way, way.tags, None)
|
||||
self.construct_way(way, way.tags, [], [way.nodes])
|
||||
|
||||
ui.progress_bar(-1, len(self.map_.way_map), text="Constructing ways")
|
||||
|
||||
def construct_way(
|
||||
self, way: Optional[OSMWay], tags: Dict[str, Any],
|
||||
path: Optional[str]) -> None:
|
||||
inners, outers) -> None:
|
||||
"""
|
||||
Way construction.
|
||||
|
||||
:param way: OSM way
|
||||
:param tags: way tag dictionary
|
||||
:param path: way path (if there is no nodes)
|
||||
"""
|
||||
assert way or path
|
||||
|
||||
layer: float = 0
|
||||
level: float = 0
|
||||
# level: float = 0
|
||||
#
|
||||
# if "layer" in tags:
|
||||
# layer = get_float(tags["layer"])
|
||||
# if "level" in tags:
|
||||
# try:
|
||||
# levels = list(map(float, tags["level"].split(";")))
|
||||
# level = sum(levels) / len(levels)
|
||||
# except ValueError:
|
||||
# pass
|
||||
|
||||
if "layer" in tags:
|
||||
layer = get_float(tags["layer"])
|
||||
if "level" in tags:
|
||||
try:
|
||||
levels = list(map(float, tags["level"].split(";")))
|
||||
level = sum(levels) / len(levels)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
layer = 100 * level + 0.01 * layer
|
||||
# layer = 100 * level + 0.01 * layer
|
||||
|
||||
nodes = None
|
||||
|
||||
center_point = None
|
||||
center_point, center_coordinates = None, None
|
||||
|
||||
if way:
|
||||
center_point = line_center(way.nodes, self.flinger)
|
||||
center_point, center_coordinates = \
|
||||
line_center(way.nodes, self.flinger)
|
||||
nodes = way.nodes
|
||||
|
||||
if self.mode == "user-coloring":
|
||||
|
@ -239,7 +281,7 @@ class Constructor:
|
|||
return
|
||||
user_color = get_user_color(way.user, self.seed)
|
||||
self.ways.append(
|
||||
Way("way", nodes, path,
|
||||
Way("way", inners, outers,
|
||||
{"fill": "none", "stroke": user_color,
|
||||
"stroke-width": 1}))
|
||||
return
|
||||
|
@ -249,7 +291,7 @@ class Constructor:
|
|||
return
|
||||
time_color = get_time_color(way.timestamp)
|
||||
self.ways.append(
|
||||
Way("way", nodes, path,
|
||||
Way("way", inners, outers,
|
||||
{"fill": "none", "stroke": time_color,
|
||||
"stroke-width": 1}))
|
||||
return
|
||||
|
@ -261,7 +303,7 @@ class Constructor:
|
|||
kind: str = "way"
|
||||
levels = None
|
||||
|
||||
if "building" in tags:
|
||||
if "building" in tags: # or "building:part" in tags:
|
||||
kind = "building"
|
||||
if "building:levels" in tags:
|
||||
try:
|
||||
|
@ -288,21 +330,31 @@ class Constructor:
|
|||
break
|
||||
if matched:
|
||||
style: Dict[str, Any] = {"fill": "none"}
|
||||
if "layer" in element:
|
||||
layer += element["layer"]
|
||||
if "priority" in element:
|
||||
layer = element["priority"]
|
||||
for key in element: # type: str
|
||||
if key not in ["tags", "no_tags", "layer", "level", "icon"]:
|
||||
if key not in ["tags", "no_tags", "priority", "level", "icon", "r", "r2"]:
|
||||
value = element[key]
|
||||
if isinstance(value, str) and value.endswith("_color"):
|
||||
value = self.scheme.get_color(value)
|
||||
style[key] = value
|
||||
if center_coordinates is not None:
|
||||
if "r" in element:
|
||||
style["stroke-width"] = \
|
||||
element["r"] * \
|
||||
self.flinger.get_scale(center_coordinates)
|
||||
if "r2" in element:
|
||||
style["stroke-width"] = \
|
||||
element["r2"] * \
|
||||
self.flinger.get_scale(center_coordinates) + 2
|
||||
self.ways.append(
|
||||
Way(kind, nodes, path, style, layer, 50, levels))
|
||||
Way(kind, inners, outers, style, layer, levels))
|
||||
if center_point is not None and \
|
||||
(way.is_cycle() or "area" in tags and tags["area"]):
|
||||
icon_set: IconSet = self.scheme.get_icon(tags)
|
||||
self.nodes.append(Node(
|
||||
icon_set, tags, center_point, path, is_for_node=False))
|
||||
icon_set, tags, center_point, center_coordinates,
|
||||
is_for_node=False))
|
||||
appended = True
|
||||
|
||||
if not appended:
|
||||
|
@ -310,12 +362,13 @@ class Constructor:
|
|||
style: Dict[str, Any] = {
|
||||
"fill": "none", "stroke": "#FF0000", "stroke-width": 1}
|
||||
self.ways.append(Way(
|
||||
kind, nodes, path, style, layer, 50, levels))
|
||||
if center_point is not None and way.is_cycle() or \
|
||||
"area" in tags and tags["area"]:
|
||||
kind, inners, outers, style, layer, levels))
|
||||
if center_point is not None and (way.is_cycle() or
|
||||
"area" in tags and tags["area"]):
|
||||
icon_set: IconSet = self.scheme.get_icon(tags)
|
||||
self.nodes.append(Node(
|
||||
icon_set, tags, center_point, path, is_for_node=False))
|
||||
icon_set, tags, center_point, center_coordinates,
|
||||
is_for_node=False))
|
||||
|
||||
def construct_relations(self) -> None:
|
||||
"""
|
||||
|
@ -327,45 +380,35 @@ class Constructor:
|
|||
if not self.check_level(tags):
|
||||
continue
|
||||
if "type" in tags and tags["type"] == "multipolygon":
|
||||
inners, outers = [], []
|
||||
inner_ways: List[OSMWay] = []
|
||||
outer_ways: List[OSMWay] = []
|
||||
for member in relation.members: # type: OSMMember
|
||||
if member.type_ == "way":
|
||||
if member.role == "inner":
|
||||
if member.ref in self.map_.way_map:
|
||||
inners.append(self.map_.way_map[member.ref])
|
||||
inner_ways.append(self.map_.way_map[member.ref])
|
||||
elif member.role == "outer":
|
||||
if member.ref in self.map_.way_map:
|
||||
outers.append(self.map_.way_map[member.ref])
|
||||
p = ""
|
||||
inners_path = glue(inners)
|
||||
outers_path = glue(outers)
|
||||
for nodes in outers_path:
|
||||
path = get_path(nodes, np.array([0, 0]), self.flinger)
|
||||
p += path + " "
|
||||
for nodes in inners_path:
|
||||
nodes.reverse()
|
||||
path = get_path(nodes, np.array([0, 0]), self.flinger)
|
||||
p += path + " "
|
||||
if p:
|
||||
self.construct_way(None, tags, p)
|
||||
outer_ways.append(self.map_.way_map[member.ref])
|
||||
inners_path: List[List[OSMNode]] = glue(inner_ways)
|
||||
outers_path: List[List[OSMNode]] = glue(outer_ways)
|
||||
self.construct_way(None, tags, inners_path, outers_path)
|
||||
|
||||
def construct_nodes(self) -> None:
|
||||
"""
|
||||
Draw nodes.
|
||||
"""
|
||||
print("Draw nodes...")
|
||||
|
||||
start_time = datetime.now()
|
||||
|
||||
node_number: int = 0
|
||||
|
||||
s = sorted(
|
||||
self.map_.node_map.keys(),
|
||||
key=lambda x: -self.map_.node_map[x].position.lat)
|
||||
key=lambda x: -self.map_.node_map[x].position[0])
|
||||
|
||||
for node_id in s: # type: int
|
||||
node_number += 1
|
||||
ui.progress_bar(node_number, len(self.map_.node_map))
|
||||
ui.progress_bar(
|
||||
node_number, len(self.map_.node_map),
|
||||
text="Constructing nodes")
|
||||
node: OSMNode = self.map_.node_map[node_id]
|
||||
flung = self.flinger.fling(node.position)
|
||||
tags = node.tags
|
||||
|
@ -385,8 +428,6 @@ class Constructor:
|
|||
if self.mode == "time":
|
||||
icon_set.color = get_time_color(node.timestamp)
|
||||
|
||||
self.nodes.append(Node(icon_set, tags, flung, None))
|
||||
self.nodes.append(Node(icon_set, tags, flung, node.position))
|
||||
|
||||
ui.progress_bar(-1, len(self.map_.node_map))
|
||||
|
||||
print("Nodes painted in " + str(datetime.now() - start_time) + ".")
|
||||
ui.progress_bar(-1, len(self.map_.node_map), text="Constructing nodes")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue