mirror of
https://github.com/enzet/map-machine.git
synced 2025-05-10 23:56:50 +02:00
Refactor scheme.
This commit is contained in:
parent
4e0394d654
commit
21acccab20
4 changed files with 159 additions and 154 deletions
|
@ -8,6 +8,7 @@ from typing import Any, Dict, List, Optional, Set
|
||||||
from roentgen import process, ui
|
from roentgen import process, ui
|
||||||
from roentgen.flinger import Geo, GeoFlinger
|
from roentgen.flinger import Geo, GeoFlinger
|
||||||
from roentgen.osm_reader import OSMMember, OSMRelation, OSMWay
|
from roentgen.osm_reader import OSMMember, OSMRelation, OSMWay
|
||||||
|
from roentgen.scheme import Scheme
|
||||||
|
|
||||||
|
|
||||||
class Node:
|
class Node:
|
||||||
|
@ -16,7 +17,7 @@ class Node:
|
||||||
"""
|
"""
|
||||||
def __init__(
|
def __init__(
|
||||||
self, shapes, tags: Dict[str, str], x: float, y: float, color: str,
|
self, shapes, tags: Dict[str, str], x: float, y: float, color: str,
|
||||||
path: Optional[str], processed, priority=0):
|
path: Optional[str], processed, priority: int = 0):
|
||||||
self.shapes = shapes
|
self.shapes = shapes
|
||||||
self.tags = tags
|
self.tags = tags
|
||||||
self.x = x
|
self.x = x
|
||||||
|
@ -165,13 +166,13 @@ class Constructor:
|
||||||
"""
|
"""
|
||||||
Röntgen node and way constructor.
|
Röntgen node and way constructor.
|
||||||
"""
|
"""
|
||||||
def __init__(self, check_level, mode, seed, map_, flinger, scheme):
|
def __init__(self, check_level, mode, seed, map_, flinger, scheme: Scheme):
|
||||||
self.check_level = check_level
|
self.check_level = check_level
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
self.seed = seed
|
self.seed = seed
|
||||||
self.map_ = map_
|
self.map_ = map_
|
||||||
self.flinger = flinger
|
self.flinger = flinger
|
||||||
self.scheme = scheme
|
self.scheme: Scheme = scheme
|
||||||
|
|
||||||
self.nodes: List[Node] = []
|
self.nodes: List[Node] = []
|
||||||
self.ways: List[Way] = []
|
self.ways: List[Way] = []
|
||||||
|
@ -180,7 +181,7 @@ class Constructor:
|
||||||
"""
|
"""
|
||||||
Get color from the scheme.
|
Get color from the scheme.
|
||||||
"""
|
"""
|
||||||
return self.scheme["colors"][name]
|
return self.scheme.get_color(name)
|
||||||
|
|
||||||
def construct_ways(self):
|
def construct_ways(self):
|
||||||
"""
|
"""
|
||||||
|
@ -323,8 +324,7 @@ class Constructor:
|
||||||
elif tags["landuse"] == "garages":
|
elif tags["landuse"] == "garages":
|
||||||
style = f"fill:#{self.color('parking_color')};stroke:none;"
|
style = f"fill:#{self.color('parking_color')};stroke:none;"
|
||||||
layer += 21
|
layer += 21
|
||||||
shapes, fill, processed = \
|
shapes, fill, processed = self.scheme.get_icon(tags)
|
||||||
process.get_icon(tags, self.scheme, "444444")
|
|
||||||
if way:
|
if way:
|
||||||
self.nodes.append(Node(
|
self.nodes.append(Node(
|
||||||
shapes, tags, c[0], c[1], fill, path, processed))
|
shapes, tags, c[0], c[1], fill, path, processed))
|
||||||
|
@ -346,8 +346,7 @@ class Constructor:
|
||||||
f"fill:#{self.color('building_color')};" \
|
f"fill:#{self.color('building_color')};" \
|
||||||
f"stroke:#{self.color('building_border_color')};" \
|
f"stroke:#{self.color('building_border_color')};" \
|
||||||
f"opacity:1.0;"
|
f"opacity:1.0;"
|
||||||
shapes, fill, processed = \
|
shapes, fill, processed = self.scheme.get_icon(tags)
|
||||||
process.get_icon(tags, self.scheme, "444444")
|
|
||||||
if "height" in tags:
|
if "height" in tags:
|
||||||
try:
|
try:
|
||||||
layer += float(tags["height"])
|
layer += float(tags["height"])
|
||||||
|
@ -368,8 +367,7 @@ class Constructor:
|
||||||
style = \
|
style = \
|
||||||
f"fill:#{self.color('parking_color')};" \
|
f"fill:#{self.color('parking_color')};" \
|
||||||
f"stroke:none;opacity:0.5;"
|
f"stroke:none;opacity:0.5;"
|
||||||
shapes, fill, processed = \
|
shapes, fill, processed = self.scheme.get_icon(tags)
|
||||||
process.get_icon(tags, self.scheme, "444444")
|
|
||||||
if way:
|
if way:
|
||||||
self.nodes.append(Node(
|
self.nodes.append(Node(
|
||||||
shapes, tags, c[0], c[1], fill, path, processed, 1))
|
shapes, tags, c[0], c[1], fill, path, processed, 1))
|
||||||
|
@ -613,14 +611,12 @@ class Constructor:
|
||||||
|
|
||||||
start_time = datetime.now()
|
start_time = datetime.now()
|
||||||
|
|
||||||
node_number = 0
|
node_number: int = 0
|
||||||
# processed_tags = 0
|
|
||||||
# skipped_tags = 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].lat)
|
||||||
|
|
||||||
for node_id in s:
|
for node_id in s: # type: int
|
||||||
node_number += 1
|
node_number += 1
|
||||||
ui.write_line(node_number, len(self.map_.node_map))
|
ui.write_line(node_number, len(self.map_.node_map))
|
||||||
node = self.map_.node_map[node_id]
|
node = self.map_.node_map[node_id]
|
||||||
|
@ -632,7 +628,7 @@ class Constructor:
|
||||||
if not self.check_level(tags):
|
if not self.check_level(tags):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
shapes, fill, processed = process.get_icon(tags, self.scheme)
|
shapes, fill, processed = self.scheme.get_icon(tags)
|
||||||
|
|
||||||
if self.mode in ["time", "user-coloring"]:
|
if self.mode in ["time", "user-coloring"]:
|
||||||
if not tags:
|
if not tags:
|
||||||
|
@ -643,27 +639,6 @@ class Constructor:
|
||||||
if self.mode == "time":
|
if self.mode == "time":
|
||||||
fill = get_time_color(node.timestamp)
|
fill = get_time_color(node.timestamp)
|
||||||
|
|
||||||
# for k in tags:
|
|
||||||
# if k in processed or self.no_draw(k):
|
|
||||||
# processed_tags += 1
|
|
||||||
# else:
|
|
||||||
# skipped_tags += 1
|
|
||||||
|
|
||||||
# for k in []: # tags:
|
|
||||||
# if to_write(k):
|
|
||||||
# draw_text(k + ": " + tags[k], x, y + 18 + text_y,
|
|
||||||
# "444444")
|
|
||||||
# text_y += 10
|
|
||||||
|
|
||||||
# if show_missing_tags:
|
|
||||||
# for k in tags:
|
|
||||||
# v = tags[k]
|
|
||||||
# if not no_draw(k) and not k in processed:
|
|
||||||
# if ("node " + k + ": " + v) in missing_tags:
|
|
||||||
# missing_tags["node " + k + ": " + v] += 1
|
|
||||||
# else:
|
|
||||||
# missing_tags["node " + k + ": " + v] = 1
|
|
||||||
|
|
||||||
if shapes == [] and tags != {}:
|
if shapes == [] and tags != {}:
|
||||||
shapes = [["no"]]
|
shapes = [["no"]]
|
||||||
|
|
||||||
|
@ -671,8 +646,5 @@ class Constructor:
|
||||||
shapes, tags, x, y, fill, None, processed))
|
shapes, tags, x, y, fill, None, processed))
|
||||||
|
|
||||||
ui.write_line(-1, len(self.map_.node_map))
|
ui.write_line(-1, len(self.map_.node_map))
|
||||||
|
|
||||||
print("Nodes painted in " + str(datetime.now() - start_time) + ".")
|
print("Nodes painted in " + str(datetime.now() - start_time) + ".")
|
||||||
# print("Tags processed: " + str(processed_tags) + ", tags skipped: " +
|
|
||||||
# str(skipped_tags) + " (" +
|
|
||||||
# str(processed_tags / float(
|
|
||||||
# processed_tags + skipped_tags) * 100) + " %).")
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ from roentgen.flinger import GeoFlinger, Geo
|
||||||
from roentgen.grid import draw_grid
|
from roentgen.grid import draw_grid
|
||||||
from roentgen.osm_getter import get_osm
|
from roentgen.osm_getter import get_osm
|
||||||
from roentgen.osm_reader import Map, OSMReader
|
from roentgen.osm_reader import Map, OSMReader
|
||||||
|
from roentgen.scheme import Scheme
|
||||||
|
|
||||||
ICONS_FILE_NAME: str = "icons/icons.svg"
|
ICONS_FILE_NAME: str = "icons/icons.svg"
|
||||||
TAGS_FILE_NAME: str = "data/tags.yml"
|
TAGS_FILE_NAME: str = "data/tags.yml"
|
||||||
|
@ -33,7 +34,7 @@ class Painter:
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, show_missing_tags, overlap, draw_nodes, mode, draw_captions,
|
self, show_missing_tags, overlap, draw_nodes, mode, draw_captions,
|
||||||
map_, flinger, svg: svgwrite.Drawing, icons, scheme):
|
map_, flinger, svg: svgwrite.Drawing, icons, scheme: Scheme):
|
||||||
|
|
||||||
self.show_missing_tags = show_missing_tags
|
self.show_missing_tags = show_missing_tags
|
||||||
self.overlap = overlap
|
self.overlap = overlap
|
||||||
|
@ -45,27 +46,7 @@ class Painter:
|
||||||
self.flinger = flinger
|
self.flinger = flinger
|
||||||
self.svg: svgwrite.Drawing = svg
|
self.svg: svgwrite.Drawing = svg
|
||||||
self.icons = icons
|
self.icons = icons
|
||||||
self.scheme = scheme
|
self.scheme: Scheme = scheme
|
||||||
|
|
||||||
def no_draw(self, key):
|
|
||||||
if key in self.scheme["tags_to_write"] or \
|
|
||||||
key in self.scheme["tags_to_skip"]:
|
|
||||||
return True
|
|
||||||
for prefix in \
|
|
||||||
self.scheme["prefix_to_write"] + self.scheme["prefix_to_skip"]:
|
|
||||||
if key[:len(prefix) + 1] == prefix + ":":
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def to_write(self, key):
|
|
||||||
if key in self.scheme["tags_to_skip"]:
|
|
||||||
return False
|
|
||||||
if key in self.scheme["tags_to_write"]:
|
|
||||||
return True
|
|
||||||
for prefix in self.scheme["prefix_to_write"]:
|
|
||||||
if key[:len(prefix) + 1] == prefix + ":":
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def draw_shapes(self, shapes, points, x, y, fill, tags, processed):
|
def draw_shapes(self, shapes, points, x, y, fill, tags, processed):
|
||||||
|
|
||||||
|
@ -379,7 +360,7 @@ class Painter:
|
||||||
y = int(float(y))
|
y = int(float(y))
|
||||||
opacity = 0.5
|
opacity = 0.5
|
||||||
stroke_width = 2.2
|
stroke_width = 2.2
|
||||||
outline_fill = self.scheme["colors"]["outline_color"]
|
outline_fill = self.scheme.get_color("outline_color")
|
||||||
if mode not in ["user-coloring", "time"]:
|
if mode not in ["user-coloring", "time"]:
|
||||||
r = int(fill[0:2], 16)
|
r = int(fill[0:2], 16)
|
||||||
g = int(fill[2:4], 16)
|
g = int(fill[2:4], 16)
|
||||||
|
@ -475,11 +456,7 @@ def main():
|
||||||
missing_tags = {}
|
missing_tags = {}
|
||||||
points = []
|
points = []
|
||||||
|
|
||||||
scheme = yaml.load(open(TAGS_FILE_NAME), Loader=yaml.FullLoader)
|
scheme = Scheme(TAGS_FILE_NAME, COLORS_FILE_NAME)
|
||||||
scheme["cache"] = {}
|
|
||||||
w3c_colors = yaml.load(open(COLORS_FILE_NAME), Loader=yaml.FullLoader)
|
|
||||||
for color_name in w3c_colors:
|
|
||||||
scheme["colors"][color_name] = w3c_colors[color_name]
|
|
||||||
|
|
||||||
flinger = GeoFlinger(min1, max1, [0, 0], [w, h])
|
flinger = GeoFlinger(min1, max1, [0, 0], [w, h])
|
||||||
|
|
||||||
|
|
|
@ -1,86 +0,0 @@
|
||||||
import copy
|
|
||||||
import re
|
|
||||||
|
|
||||||
from typing import Any, Dict
|
|
||||||
|
|
||||||
|
|
||||||
STANDARD_COLOR: str = "444444"
|
|
||||||
|
|
||||||
|
|
||||||
def get_color(color: str, scheme: Dict[str, Any]):
|
|
||||||
if color in scheme["colors"]: # type: str
|
|
||||||
return scheme["colors"][color]
|
|
||||||
else:
|
|
||||||
m = re.match("^#?(?P<color1>[0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f])" +
|
|
||||||
"(?P<color2>[0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f])?$", color)
|
|
||||||
if m:
|
|
||||||
if "color2" in m.groups():
|
|
||||||
return m.group("color1") + m.group("color2")
|
|
||||||
else:
|
|
||||||
return "".join(map(lambda x: x + x, m.group("color1")))
|
|
||||||
return STANDARD_COLOR
|
|
||||||
|
|
||||||
|
|
||||||
def get_icon(
|
|
||||||
tags: Dict[str, Any], scheme: Dict[str, Any],
|
|
||||||
fill: str = STANDARD_COLOR):
|
|
||||||
|
|
||||||
tags_hash = ",".join(tags.keys()) + ":" + \
|
|
||||||
",".join(map(lambda x: str(x), tags.values()))
|
|
||||||
if tags_hash in scheme["cache"]:
|
|
||||||
return scheme["cache"][tags_hash]
|
|
||||||
main_icon = None
|
|
||||||
extra_icons = []
|
|
||||||
processed = set()
|
|
||||||
for matcher in scheme["tags"]:
|
|
||||||
matched = True
|
|
||||||
for key in matcher["tags"]:
|
|
||||||
if key not in tags:
|
|
||||||
matched = False
|
|
||||||
break
|
|
||||||
if matcher["tags"][key] != "*" and \
|
|
||||||
matcher["tags"][key] != tags[key]:
|
|
||||||
matched = False
|
|
||||||
break
|
|
||||||
if "no_tags" in matcher:
|
|
||||||
for no_tag in matcher["no_tags"]:
|
|
||||||
if no_tag in tags.keys():
|
|
||||||
matched = False
|
|
||||||
break
|
|
||||||
if matched:
|
|
||||||
if "draw" in matcher and not matcher["draw"]:
|
|
||||||
processed |= set(matcher["tags"].keys())
|
|
||||||
if "icon" in matcher:
|
|
||||||
main_icon = copy.deepcopy(matcher["icon"])
|
|
||||||
processed |= set(matcher["tags"].keys())
|
|
||||||
if "over_icon" in matcher:
|
|
||||||
if main_icon: # TODO: check main icon in under icons
|
|
||||||
main_icon += matcher["over_icon"]
|
|
||||||
for key in matcher["tags"].keys():
|
|
||||||
processed.add(key)
|
|
||||||
if "add_icon" in matcher:
|
|
||||||
extra_icons += matcher["add_icon"]
|
|
||||||
for key in matcher["tags"].keys():
|
|
||||||
processed.add(key)
|
|
||||||
if "color" in matcher:
|
|
||||||
fill = scheme["colors"][matcher["color"]]
|
|
||||||
for key in matcher["tags"].keys():
|
|
||||||
processed.add(key)
|
|
||||||
|
|
||||||
for color_name in ["color", "colour", "building:colour"]:
|
|
||||||
if color_name in tags:
|
|
||||||
fill = get_color(tags[color_name], scheme)
|
|
||||||
if fill != STANDARD_COLOR:
|
|
||||||
processed.add(color_name)
|
|
||||||
else:
|
|
||||||
print(f"No color {tags[color_name]}.")
|
|
||||||
|
|
||||||
if main_icon:
|
|
||||||
returned = [main_icon] + extra_icons, fill, processed
|
|
||||||
else:
|
|
||||||
returned = extra_icons, fill, processed
|
|
||||||
|
|
||||||
scheme["cache"][tags_hash] = returned
|
|
||||||
|
|
||||||
return returned
|
|
||||||
|
|
142
roentgen/scheme.py
Normal file
142
roentgen/scheme.py
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
import copy
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
|
DEFAULT_COLOR: str = "444444"
|
||||||
|
|
||||||
|
|
||||||
|
class Scheme:
|
||||||
|
"""
|
||||||
|
Map style.
|
||||||
|
|
||||||
|
Specifies map colors and rules to draw icons for OpenStreetMap tags.
|
||||||
|
"""
|
||||||
|
def __init__(self, file_name: str, color_file_name: str):
|
||||||
|
"""
|
||||||
|
:param file_name: scheme file name with tags, colors, and tag key
|
||||||
|
specification
|
||||||
|
:param color_file_name: additional color scheme
|
||||||
|
"""
|
||||||
|
content: Dict[str, Any] = \
|
||||||
|
yaml.load(open(file_name).read(), Loader=yaml.FullLoader)
|
||||||
|
|
||||||
|
self.tags: List[Dict[str, Any]] = content["tags"]
|
||||||
|
|
||||||
|
self.colors: Dict[str, str] = content["colors"]
|
||||||
|
w3c_colors: Dict[str, str] = \
|
||||||
|
yaml.load(open(color_file_name), Loader=yaml.FullLoader)
|
||||||
|
|
||||||
|
self.colors.update(w3c_colors)
|
||||||
|
|
||||||
|
self.tags_to_write: List[str] = content["tags_to_write"]
|
||||||
|
self.prefix_to_write: List[str] = content["prefix_to_write"]
|
||||||
|
self.tags_to_skip: List[str] = content["tags_to_skip"]
|
||||||
|
self.prefix_to_skip: List[str] = content["prefix_to_skip"]
|
||||||
|
|
||||||
|
self.cache = {}
|
||||||
|
|
||||||
|
def get_color(self, color: str) -> str:
|
||||||
|
"""
|
||||||
|
Return color if the color is in scheme, otherwise return default color.
|
||||||
|
|
||||||
|
:return: 6-digit color specification without "#"
|
||||||
|
"""
|
||||||
|
if color in self.colors:
|
||||||
|
return self.colors[color]
|
||||||
|
if color.startswith("#"):
|
||||||
|
return color[1:]
|
||||||
|
|
||||||
|
print(f"No color {color}.")
|
||||||
|
|
||||||
|
return DEFAULT_COLOR
|
||||||
|
|
||||||
|
def is_no_drawable(self, key: str) -> bool:
|
||||||
|
"""
|
||||||
|
Return true if key is specified as no drawable (should not be
|
||||||
|
represented on the map as icon set or as text) by the scheme.
|
||||||
|
|
||||||
|
:param key: OpenStreetMap tag key
|
||||||
|
"""
|
||||||
|
if key in self.tags_to_write or key in self.tags_to_skip:
|
||||||
|
return True
|
||||||
|
for prefix in self.prefix_to_write + self.prefix_to_skip: # type: str
|
||||||
|
if key[:len(prefix) + 1] == prefix + ":":
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def is_writable(self, key: str) -> bool:
|
||||||
|
"""
|
||||||
|
Return true if key is specified as writable (should be represented on
|
||||||
|
the map as text) by the scheme.
|
||||||
|
|
||||||
|
:param key: OpenStreetMap tag key
|
||||||
|
"""
|
||||||
|
if key in self.tags_to_skip:
|
||||||
|
return False
|
||||||
|
if key in self.tags_to_write:
|
||||||
|
return True
|
||||||
|
for prefix in self.prefix_to_write:
|
||||||
|
if key[:len(prefix) + 1] == prefix + ":":
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_icon(self, tags: Dict[str, Any]):
|
||||||
|
|
||||||
|
tags_hash = ",".join(tags.keys()) + ":" + \
|
||||||
|
",".join(map(lambda x: str(x), tags.values()))
|
||||||
|
if tags_hash in self.cache:
|
||||||
|
return self.cache[tags_hash]
|
||||||
|
main_icon = None
|
||||||
|
extra_icons = []
|
||||||
|
processed = set()
|
||||||
|
fill = DEFAULT_COLOR
|
||||||
|
for matcher in self.tags:
|
||||||
|
matched = True
|
||||||
|
for key in matcher["tags"]:
|
||||||
|
if key not in tags:
|
||||||
|
matched = False
|
||||||
|
break
|
||||||
|
if matcher["tags"][key] != "*" and \
|
||||||
|
matcher["tags"][key] != tags[key]:
|
||||||
|
matched = False
|
||||||
|
break
|
||||||
|
if "no_tags" in matcher:
|
||||||
|
for no_tag in matcher["no_tags"]:
|
||||||
|
if no_tag in tags.keys():
|
||||||
|
matched = False
|
||||||
|
break
|
||||||
|
if matched:
|
||||||
|
if "draw" in matcher and not matcher["draw"]:
|
||||||
|
processed |= set(matcher["tags"].keys())
|
||||||
|
if "icon" in matcher:
|
||||||
|
main_icon = copy.deepcopy(matcher["icon"])
|
||||||
|
processed |= set(matcher["tags"].keys())
|
||||||
|
if "over_icon" in matcher:
|
||||||
|
if main_icon: # TODO: check main icon in under icons
|
||||||
|
main_icon += matcher["over_icon"]
|
||||||
|
for key in matcher["tags"].keys():
|
||||||
|
processed.add(key)
|
||||||
|
if "add_icon" in matcher:
|
||||||
|
extra_icons += matcher["add_icon"]
|
||||||
|
for key in matcher["tags"].keys():
|
||||||
|
processed.add(key)
|
||||||
|
if "color" in matcher:
|
||||||
|
fill = self.colors[matcher["color"]]
|
||||||
|
for key in matcher["tags"].keys():
|
||||||
|
processed.add(key)
|
||||||
|
|
||||||
|
for color_name in ["color", "colour", "building:colour"]:
|
||||||
|
if color_name in tags:
|
||||||
|
fill = self.get_color(tags[color_name])
|
||||||
|
processed.add(color_name)
|
||||||
|
|
||||||
|
if main_icon:
|
||||||
|
returned = [main_icon] + extra_icons, fill, processed
|
||||||
|
else:
|
||||||
|
returned = extra_icons, fill, processed
|
||||||
|
|
||||||
|
self.cache[tags_hash] = returned
|
||||||
|
|
||||||
|
return returned
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue