Refactor constructor; change code style.
4
.idea/Roentgen.iml
generated
|
@ -5,10 +5,6 @@
|
|||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="PyDocumentationSettings">
|
||||
<option name="format" value="PLAIN" />
|
||||
<option name="myDocStringFormat" value="Plain" />
|
||||
</component>
|
||||
<component name="TestRunnerService">
|
||||
<option name="PROJECT_TEST_RUNNER" value="pytest" />
|
||||
</component>
|
||||
|
|
288
data/tags.yml
|
@ -18,6 +18,7 @@ colors:
|
|||
|
||||
outline_color: "FFFFFF"
|
||||
beach_color: "F0E0C0"
|
||||
boundary_color: "880088"
|
||||
building_color: "F8F0E8" # "D0D0C0"
|
||||
building_border_color: "DDDDDD" # "AAAAAA"
|
||||
construction_color: "CCCCCC"
|
||||
|
@ -232,6 +233,8 @@ tags:
|
|||
icon: [cupcake]
|
||||
- tags: {shop: mall}
|
||||
icon: [bag]
|
||||
- tags: {shop: alcohol}
|
||||
icon: [bottle]
|
||||
- tags: {shop: mall, building: 'yes'}
|
||||
icon: [bag]
|
||||
- tags: {shop: convenience}
|
||||
|
@ -527,6 +530,291 @@ tags:
|
|||
- tags: {'payment:credit_cards': 'yes'}
|
||||
add_icon: [credit_card]
|
||||
|
||||
ways:
|
||||
- tags: {indoor: area}
|
||||
stroke: indoor_border_color
|
||||
stroke-width: 1
|
||||
fill: indoor_color
|
||||
layer: 10
|
||||
- tags: {indoor: corridor}
|
||||
stroke: indoor_color
|
||||
stroke-width: 1
|
||||
fill: indoor_color
|
||||
layer: 11
|
||||
- tags: {indoor: ["yes", room, elevator]}
|
||||
stroke: indoor_color
|
||||
stroke-width: 1
|
||||
fill: indoor_color
|
||||
layer: 12
|
||||
- tags: {indoor: column}
|
||||
stroke: indoor_color
|
||||
stroke-width: 1
|
||||
fill: indoor_color
|
||||
layer: 13
|
||||
|
||||
- tags: {natural: wood}
|
||||
fill: wood_color
|
||||
layer: 21
|
||||
- tags: {natural: grassland}
|
||||
fill: grass_color
|
||||
stroke: grass_border_color
|
||||
layer: 20
|
||||
- tags: {natural: scrub}
|
||||
fill: wood_color
|
||||
layer: 21
|
||||
- tags: {natural: sand}
|
||||
fill: sand_color
|
||||
layer: 20
|
||||
- tags: {natural: beach}
|
||||
fill: beach_color
|
||||
layer: 20
|
||||
- tags: {natural: desert}
|
||||
fill: desert_color
|
||||
layer: 20
|
||||
- tags: {natural: forest}
|
||||
fill: wood_color
|
||||
layer: 21
|
||||
- tags: {natural: tree_row}
|
||||
layer: 21
|
||||
stroke: wood_color
|
||||
stroke-width: 5
|
||||
- tags: {natural: water}
|
||||
fill: water_color
|
||||
stroke: water_border_color
|
||||
stroke-width: 1
|
||||
layer: 21
|
||||
|
||||
- tags: {landuse: grass}
|
||||
fill: grass_color
|
||||
layer: 20
|
||||
stroke: grass_border_color
|
||||
- tags: {landuse: conservation}
|
||||
fill: grass_color
|
||||
layer: 20
|
||||
- tags: {landuse: forest}
|
||||
fill: wood_color
|
||||
layer: 20
|
||||
- tags: {landuse: garages}
|
||||
fill: parking_color
|
||||
layer: 21
|
||||
- tags: {landuse: construction}
|
||||
fill: construction_color
|
||||
- tags: {landuse: residential}
|
||||
fill: none
|
||||
stroke: none
|
||||
- tags: {landuse: commercial}
|
||||
fill: none
|
||||
stroke: none
|
||||
|
||||
- tags: {building: "*"}
|
||||
fill: building_color
|
||||
stroke: building_border_color
|
||||
|
||||
- tags: {amenity: parking}
|
||||
fill: parking_color
|
||||
opacity: 0.5
|
||||
icon: parking
|
||||
|
||||
- tags: {waterway: riverbank}
|
||||
fill: water_color
|
||||
stroke: water_border_color
|
||||
stroke-width: 1
|
||||
|
||||
- tags: {railway: subway}
|
||||
stroke-width: 10
|
||||
stroke: "#DDDDDD"
|
||||
layer: 41
|
||||
- tags: {railway: [narrow_gauge, tram]}
|
||||
stroke-width: 2
|
||||
stroke: "#000000"
|
||||
layer: 41
|
||||
- tags: {railway: platform}
|
||||
fill: platform_color
|
||||
stroke-width: 1
|
||||
stroke: platform_border_color
|
||||
layer: 41
|
||||
|
||||
- tags: {highway: motorway}
|
||||
stroke-width: 33
|
||||
stroke: road_border_color
|
||||
layer: 41
|
||||
- tags: {highway: trunk}
|
||||
stroke-width: 31
|
||||
stroke: road_border_color
|
||||
layer: 41
|
||||
- tags: {highway: primary}
|
||||
stroke-width: 29
|
||||
stroke: primary_border_color
|
||||
layer: 41
|
||||
- tags: {highway: secondary}
|
||||
stroke-width: 27
|
||||
stroke: road_border_color
|
||||
layer: 41
|
||||
- tags: {highway: tertiary}
|
||||
stroke-width: 25
|
||||
stroke: road_border_color
|
||||
layer: 41
|
||||
- tags: {highway: unclassified}
|
||||
stroke-width: 17
|
||||
stroke: road_border_color
|
||||
layer: 41
|
||||
- tags: {highway: residential}
|
||||
stroke-width: 17
|
||||
stroke: road_border_color
|
||||
layer: 41
|
||||
- tags: {highway: service}
|
||||
no_tags: {service: parking_aisle}
|
||||
stroke-width: 11
|
||||
stroke: road_border_color
|
||||
layer: 41
|
||||
- tags: {highway: service, service: parking_aisle}
|
||||
stroke-width: 7
|
||||
stroke: road_border_color
|
||||
layer: 41
|
||||
- tags: {highway: track}
|
||||
stroke-width: 3
|
||||
stroke: road_border_color
|
||||
layer: 41
|
||||
- tags: {highway: [footway, pedestrian, cycleway]}
|
||||
no_tags: {area: "yes"}
|
||||
stroke-width: 3
|
||||
stroke: foot_border_color
|
||||
layer: 41
|
||||
- tags: {highway: steps}
|
||||
stroke-width: 6
|
||||
stroke: foot_border_color
|
||||
|
||||
- tags: {highway: motorway}
|
||||
stroke-width: 31
|
||||
stroke: "#FFFFFF"
|
||||
layer: 42
|
||||
- tags: {highway: trunk}
|
||||
stroke-width: 29
|
||||
stroke: "#FFFFFF"
|
||||
layer: 42
|
||||
- tags: {highway: primary}
|
||||
stroke-width: 27
|
||||
stroke: primary_color
|
||||
layer: 42
|
||||
- tags: {highway: secondary}
|
||||
stroke-width: 25
|
||||
stroke: "#FFFFFF"
|
||||
layer: 42
|
||||
- tags: {highway: tertiary}
|
||||
stroke-width: 23
|
||||
stroke: "#FFFFFF"
|
||||
layer: 42
|
||||
- tags: {highway: unclassified}
|
||||
stroke-width: 15
|
||||
stroke: "#FFFFFF"
|
||||
layer: 42
|
||||
- tags: {highway: residential}
|
||||
stroke-width: 15
|
||||
stroke: "#FFFFFF"
|
||||
layer: 42
|
||||
- tags: {highway: service, service: parking_aisle}
|
||||
stroke-width: 5
|
||||
stroke: "#FFFFFF"
|
||||
layer: 42
|
||||
- tags: {highway: service}
|
||||
no_tags: {service: parking_aisle}
|
||||
stroke-width: 9
|
||||
stroke: "#FFFFFF"
|
||||
layer: 42
|
||||
- tags: {highway: track}
|
||||
stroke-width: 3
|
||||
stroke: road_border_color
|
||||
layer: 42
|
||||
- tags: {highway: [footway, pedestrian]}
|
||||
no_tags: {area: "yes"}
|
||||
stroke-width: 1.5
|
||||
stroke-dasharray: 7,3
|
||||
stroke-linecap: round
|
||||
stroke: foot_color
|
||||
layer: 42
|
||||
- tags: {highway: [footway, pedestrian], area: "yes"}
|
||||
stroke: none
|
||||
fill: "#DDDDDD"
|
||||
layer: -55 # FIXME
|
||||
- tags: {highway: cycleway}
|
||||
no_tags: {area: "yes"}
|
||||
stroke-width: 1
|
||||
stroke: cycle_color
|
||||
stroke-dasharray: 8,2
|
||||
stroke-linecap: butt
|
||||
layer: 42
|
||||
- tags: {highway: steps, conveying: "*"}
|
||||
stroke-width: 5
|
||||
stroke-dasharray: 1.5,2
|
||||
stroke-linecap: butt
|
||||
stroke: "#888888"
|
||||
layer: 42
|
||||
- tags: {highway: steps}
|
||||
no_tags: {conveying: "*"}
|
||||
stroke-width: 5
|
||||
stroke-dasharray: 1.5,2
|
||||
stroke-linecap: butt
|
||||
stroke: foot_color
|
||||
layer: 42
|
||||
- tags: {highway: path}
|
||||
stroke-width: 1
|
||||
stroke-dasharray: 5,5
|
||||
stroke-linecap: butt
|
||||
stroke: foot_color
|
||||
layer: 42
|
||||
|
||||
- tags: {leisure: playground}
|
||||
fill: playground_color
|
||||
opacity: 0.2
|
||||
icon: toy_horse
|
||||
layer: 21
|
||||
- tags: {leisure: garden}
|
||||
fill: grass_color
|
||||
layer: 21
|
||||
- tags: {leisure: pitch}
|
||||
fill: playground_color
|
||||
opacity: 0.2
|
||||
layer: 21
|
||||
- tags: {leisure: park}
|
||||
fill: grass_color
|
||||
opacity: 0.5
|
||||
|
||||
- tags: {barrier: hedge}
|
||||
fill: none
|
||||
stroke: wood_color
|
||||
stroke-width: 4
|
||||
layer: 40
|
||||
- tags: {barrier: [fence, retaining_wall]}
|
||||
fill: none
|
||||
stroke: "#000000"
|
||||
stroke-width: 1
|
||||
opacity: 0.4
|
||||
layer: 40
|
||||
- tags: {barrier: handrail}
|
||||
fill: none
|
||||
stroke: "#000000"
|
||||
stroke-width: 1
|
||||
opacity: 0.3
|
||||
layer: 40
|
||||
- tags: {barrier: kerb}
|
||||
fill: none
|
||||
stroke: "#000000"
|
||||
stroke-width: 1
|
||||
opacity: 0.2
|
||||
layer: 40
|
||||
|
||||
- tags: {border: "*"}
|
||||
stroke: "#FF0000"
|
||||
stroke-width: 0.5
|
||||
stroke-dasharray: 10,20
|
||||
- tags: {"area:highway": "*"}
|
||||
|
||||
- tags: {boundary: "*"}
|
||||
stroke: boundary_color
|
||||
stroke-width: 1
|
||||
stroke-dasharray: 10,5
|
||||
layer: 60
|
||||
|
||||
tags_to_write: [
|
||||
"operator", "opening_hours", "cuisine", "network", "website",
|
||||
"website_2", "STIF:zone", "opening_hours:url", "phone",
|
||||
|
|
Before Width: | Height: | Size: 197 KiB After Width: | Height: | Size: 199 KiB |
BIN
doc/grid.png
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 44 KiB |
BIN
doc/time.png
Before Width: | Height: | Size: 432 KiB After Width: | Height: | Size: 416 KiB |
BIN
doc/trees.png
Before Width: | Height: | Size: 171 KiB After Width: | Height: | Size: 171 KiB |
BIN
doc/user.png
Before Width: | Height: | Size: 410 KiB After Width: | Height: | Size: 410 KiB |
|
@ -5,10 +5,11 @@ from hashlib import sha256
|
|||
from datetime import datetime
|
||||
from typing import Any, Dict, List, Optional, Set
|
||||
|
||||
from roentgen import process, ui
|
||||
from roentgen import ui
|
||||
from roentgen.extract_icon import DEFAULT_SMALL_SHAPE_ID
|
||||
from roentgen.flinger import Geo, GeoFlinger
|
||||
from roentgen.osm_reader import OSMMember, OSMRelation, OSMWay
|
||||
from roentgen.scheme import Scheme
|
||||
from roentgen.scheme import IconSet, Scheme
|
||||
|
||||
|
||||
class Node:
|
||||
|
@ -16,17 +17,16 @@ class Node:
|
|||
Node in Röntgen terms.
|
||||
"""
|
||||
def __init__(
|
||||
self, shapes, tags: Dict[str, str], x: float, y: float, color: str,
|
||||
path: Optional[str], processed, priority: int = 0):
|
||||
self.shapes = shapes
|
||||
self, icon_set: IconSet, tags: Dict[str, str],
|
||||
point: (float, float), path: Optional[str],
|
||||
priority: int = 0, is_for_node: bool = True):
|
||||
self.icon_set: IconSet = icon_set
|
||||
self.tags = tags
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.color = color
|
||||
self.point = point
|
||||
self.path = path
|
||||
self.processed = processed
|
||||
self.priority = priority
|
||||
self.layer = 0
|
||||
self.is_for_node = is_for_node
|
||||
|
||||
|
||||
class Way:
|
||||
|
@ -34,12 +34,12 @@ class Way:
|
|||
Way in Röntgen terms.
|
||||
"""
|
||||
def __init__(
|
||||
self, kind: str, nodes, path, style, layer: float = 0.0,
|
||||
priority: float = 0, levels=None):
|
||||
self, kind: str, nodes, path, style: Dict[str, Any],
|
||||
layer: float = 0.0, priority: float = 0, levels=None):
|
||||
self.kind = kind
|
||||
self.nodes = nodes
|
||||
self.path = path
|
||||
self.style = style
|
||||
self.style: Dict[str, Any] = style
|
||||
self.layer = layer
|
||||
self.priority = priority
|
||||
self.levels = levels
|
||||
|
@ -219,8 +219,10 @@ class Constructor:
|
|||
|
||||
nodes = None
|
||||
|
||||
center_point = None
|
||||
|
||||
if way:
|
||||
c = line_center(
|
||||
center_point = line_center(
|
||||
map(lambda x: self.map_.node_map[x], way.nodes), self.flinger)
|
||||
nodes = way.nodes
|
||||
|
||||
|
@ -230,8 +232,8 @@ class Constructor:
|
|||
user_color = get_user_color(way.user, self.seed)
|
||||
self.ways.append(
|
||||
Way("way", nodes, path,
|
||||
f"fill:none;stroke:#{user_color};"
|
||||
f"stroke-width:1;"))
|
||||
{"fill": "none", "stroke": "#" + user_color,
|
||||
"stroke-width": 1}))
|
||||
return
|
||||
|
||||
if self.mode == "time":
|
||||
|
@ -240,339 +242,69 @@ class Constructor:
|
|||
time_color = get_time_color(way.timestamp)
|
||||
self.ways.append(
|
||||
Way("way", nodes, path,
|
||||
f"fill:none;stroke:#{time_color};"
|
||||
f"stroke-width:1;"))
|
||||
{"fill": "none", "stroke": "#" + time_color,
|
||||
"stroke-width": 1}))
|
||||
return
|
||||
|
||||
# Indoor features
|
||||
if not tags:
|
||||
return
|
||||
|
||||
if "indoor" in tags:
|
||||
v = tags["indoor"]
|
||||
style = \
|
||||
f"stroke:#{self.color('indoor_border_color')};" \
|
||||
f"stroke-width:1;"
|
||||
if v == "area":
|
||||
style += f"fill:#{self.color('indoor_color')};"
|
||||
layer += 10
|
||||
elif v == "corridor":
|
||||
style += f"fill:#{self.color('indoor_color')};"
|
||||
layer += 11
|
||||
elif v in ["yes", "room", "elevator"]:
|
||||
style += f"fill:#{self.color('indoor_color')};"
|
||||
layer += 12
|
||||
elif v == "column":
|
||||
style += f"fill:#{self.color('indoor_border_color')};"
|
||||
layer += 13
|
||||
self.ways.append(Way("way", nodes, path, style, layer, 50))
|
||||
|
||||
# Natural
|
||||
|
||||
if "natural" in tags:
|
||||
v = tags["natural"]
|
||||
style = "stroke:none;"
|
||||
if v == "wood":
|
||||
style += f"fill:#{self.color('wood_color')};"
|
||||
layer += 21
|
||||
elif v == "grassland":
|
||||
style = \
|
||||
f"fill:#{self.color('grass_color')};" \
|
||||
f"stroke:#{self.color('grass_border_color')};"
|
||||
layer += 20
|
||||
elif v == "scrub":
|
||||
style += f"fill:#{self.color('wood_color')};"
|
||||
layer += 21
|
||||
elif v == "sand":
|
||||
style += f"fill:#{self.color('sand_color')};"
|
||||
layer += 20
|
||||
elif v == "beach":
|
||||
style += f"fill:#{self.color('beach_color')};"
|
||||
layer += 20
|
||||
elif v == "desert":
|
||||
style += f"fill:#{self.color('desert_color')};"
|
||||
layer += 20
|
||||
elif v == "forest":
|
||||
style += f"fill:#{self.color('wood_color')};"
|
||||
layer += 21
|
||||
elif v == "tree_row":
|
||||
style += \
|
||||
f"fill:none;stroke:#{self.color('wood_color')};" \
|
||||
f"stroke-width:5;"
|
||||
layer += 21
|
||||
elif v == "water":
|
||||
style = \
|
||||
f"fill:#{self.color('water_color')};" \
|
||||
f"stroke:#{self.color('water_border_color')};" \
|
||||
f"stroke-width:1.0;"
|
||||
layer += 21
|
||||
self.ways.append(Way("way", nodes, path, style, layer, 50))
|
||||
|
||||
# Landuse
|
||||
|
||||
if "landuse" in tags:
|
||||
style = "fill:none;stroke:none;"
|
||||
if tags["landuse"] == "grass":
|
||||
style = \
|
||||
f"fill:#{self.color('grass_color')};" \
|
||||
f"stroke:#{self.color('grass_border_color')};"
|
||||
layer += 20
|
||||
elif tags["landuse"] == "conservation":
|
||||
style = f"fill:#{self.color('grass_color')};stroke:none;"
|
||||
layer += 20
|
||||
elif tags["landuse"] == "forest":
|
||||
style = f"fill:#{self.color('wood_color')};stroke:none;"
|
||||
layer += 20
|
||||
elif tags["landuse"] == "garages":
|
||||
style = f"fill:#{self.color('parking_color')};stroke:none;"
|
||||
layer += 21
|
||||
shapes, fill, processed = self.scheme.get_icon(tags)
|
||||
if way:
|
||||
self.nodes.append(Node(
|
||||
shapes, tags, c[0], c[1], fill, path, processed))
|
||||
elif tags["landuse"] == "construction":
|
||||
layer += 20
|
||||
style = f"fill:#{self.color('construction_color')};stroke:none;"
|
||||
elif tags["landuse"] in ["residential", "commercial"]:
|
||||
return
|
||||
self.ways.append(Way("way", nodes, path, style, layer, 50))
|
||||
|
||||
# Building
|
||||
appended = False
|
||||
kind: str = "way"
|
||||
levels = None
|
||||
|
||||
if "building" in tags:
|
||||
layer += 40
|
||||
levels = 1
|
||||
if "building:levels" in tags:
|
||||
levels = float(tags["building:levels"])
|
||||
style = \
|
||||
f"fill:#{self.color('building_color')};" \
|
||||
f"stroke:#{self.color('building_border_color')};" \
|
||||
f"opacity:1.0;"
|
||||
shapes, fill, processed = self.scheme.get_icon(tags)
|
||||
if "height" in tags:
|
||||
try:
|
||||
layer += float(tags["height"])
|
||||
except ValueError:
|
||||
pass
|
||||
if way:
|
||||
self.nodes.append(
|
||||
Node(shapes, tags, c[0], c[1], fill, path, processed, 1))
|
||||
self.ways.append(Way(
|
||||
"building", nodes, path, style, layer, 50, levels))
|
||||
kind = "building"
|
||||
if "building:levels" in tags:
|
||||
levels = float(tags["building:levels"])
|
||||
|
||||
# Amenity
|
||||
|
||||
if "amenity" in tags:
|
||||
style = "fill:none;stroke:none;"
|
||||
layer += 21
|
||||
if tags["amenity"] == "parking":
|
||||
style = \
|
||||
f"fill:#{self.color('parking_color')};" \
|
||||
f"stroke:none;opacity:0.5;"
|
||||
shapes, fill, processed = self.scheme.get_icon(tags)
|
||||
if way:
|
||||
for element in self.scheme.ways: # type: Dict[str, Any]
|
||||
matched: bool = True
|
||||
for config_tag_key in element["tags"]: # type: str
|
||||
matcher = element["tags"][config_tag_key]
|
||||
if config_tag_key not in tags or \
|
||||
(matcher != "*" and
|
||||
tags[config_tag_key] != matcher and
|
||||
tags[config_tag_key] not in matcher):
|
||||
matched = False
|
||||
break
|
||||
if "no_tags" in element:
|
||||
for config_tag_key in element["no_tags"]: # type: str
|
||||
if config_tag_key in tags and \
|
||||
tags[config_tag_key] == \
|
||||
element["no_tags"][config_tag_key]:
|
||||
matched = False
|
||||
break
|
||||
if matched:
|
||||
style: Dict[str, Any] = {"fill": "none"}
|
||||
if "layer" in element:
|
||||
layer += element["layer"]
|
||||
for key in element: # type: str
|
||||
if key not in ["tags", "no_tags", "layer", "level", "icon"]:
|
||||
value = element[key]
|
||||
if isinstance(value, str) and value.endswith("_color"):
|
||||
value = "#" + self.scheme.get_color(value)
|
||||
style[key] = value
|
||||
self.ways.append(
|
||||
Way(kind, nodes, path, style, layer, 50, levels))
|
||||
if center_point and way.is_cycle() or \
|
||||
"area" in tags and tags["area"]:
|
||||
icon_set: IconSet = self.scheme.get_icon(tags)
|
||||
self.nodes.append(Node(
|
||||
shapes, tags, c[0], c[1], fill, path, processed, 1))
|
||||
self.ways.append(Way("way", nodes, path, style, layer, 50))
|
||||
icon_set, tags, center_point, path, is_for_node=False))
|
||||
appended = True
|
||||
|
||||
# Waterway
|
||||
if not appended:
|
||||
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 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))
|
||||
|
||||
if "waterway" in tags:
|
||||
style = "fill:none;stroke:none;"
|
||||
layer += 21
|
||||
if tags["waterway"] == "riverbank":
|
||||
style = \
|
||||
f"fill:#{self.color('water_color')};" \
|
||||
f"stroke:#{self.color('water_border_color')};" \
|
||||
f"stroke-width:1.0;"
|
||||
elif tags["waterway"] == "river":
|
||||
style = \
|
||||
f"fill:none;stroke:#{self.color('water_color')};" \
|
||||
f"stroke-width:10.0;"
|
||||
self.ways.append(Way("way", nodes, path, style, layer, 50))
|
||||
|
||||
# Railway
|
||||
|
||||
if "railway" in tags:
|
||||
layer += 41
|
||||
v = tags["railway"]
|
||||
style = \
|
||||
"fill:none;stroke-dasharray:none;stroke-linejoin:round;" \
|
||||
"stroke-linecap:round;stroke-width:"
|
||||
if v == "subway":
|
||||
style += "10;stroke:#DDDDDD;"
|
||||
if v in ["narrow_gauge", "tram"]:
|
||||
style += "2;stroke:#000000;"
|
||||
if v == "platform":
|
||||
style = \
|
||||
f"fill:#{self.color('platform_color')};" \
|
||||
f"stroke:#{self.color('platform_border_color')};" \
|
||||
f"stroke-width:1;"
|
||||
else:
|
||||
return
|
||||
self.ways.append(Way("way", nodes, path, style, layer, 50))
|
||||
|
||||
# Highway
|
||||
|
||||
if "highway" in tags:
|
||||
layer += 42
|
||||
v = tags["highway"]
|
||||
style = \
|
||||
f"fill:none;stroke:#{self.color('road_border_color')};" \
|
||||
f"stroke-dasharray:none;stroke-linejoin:round;" \
|
||||
f"stroke-linecap:round;stroke-width:"
|
||||
|
||||
# Highway outline
|
||||
|
||||
if v == "motorway":
|
||||
style += "33"
|
||||
elif v == "trunk":
|
||||
style += "31"
|
||||
elif v == "primary":
|
||||
style += f"29;stroke:#{self.color('primary_border_color')};"
|
||||
elif v == "secondary":
|
||||
style += "27"
|
||||
elif v == "tertiary":
|
||||
style += "25"
|
||||
elif v == "unclassified":
|
||||
style += "17"
|
||||
elif v == "residential":
|
||||
style += "17"
|
||||
elif v == "service":
|
||||
if "service" in tags and tags["service"] == "parking_aisle":
|
||||
style += "7"
|
||||
else:
|
||||
style += "11"
|
||||
elif v == "track":
|
||||
style += "3"
|
||||
elif v in ["footway", "pedestrian", "cycleway"]:
|
||||
if not ("area" in tags and tags["area"] == "yes"):
|
||||
style += f"3;stroke:#{self.color('foot_border_color')};"
|
||||
elif v in ["steps"]:
|
||||
style += \
|
||||
f"6;stroke:#{self.color('foot_border_color')};" \
|
||||
f"stroke-linecap:butt;"
|
||||
else:
|
||||
style = None
|
||||
if style:
|
||||
style += ";"
|
||||
self.ways.append(Way(
|
||||
"way", nodes, path, style, layer + 41, 50))
|
||||
|
||||
# Highway main shape
|
||||
|
||||
style = "fill:none;stroke:#FFFFFF;stroke-linecap:round;" + \
|
||||
"stroke-linejoin:round;stroke-width:"
|
||||
|
||||
if v == "motorway":
|
||||
style += "31"
|
||||
elif v == "trunk":
|
||||
style += "29"
|
||||
elif v == "primary":
|
||||
style += "27;stroke:#" + self.color('primary_color')
|
||||
elif v == "secondary":
|
||||
style += "25"
|
||||
elif v == "tertiary":
|
||||
style += "23"
|
||||
elif v == "unclassified":
|
||||
style += "15"
|
||||
elif v == "residential":
|
||||
style += "15"
|
||||
elif v == "service":
|
||||
if "service" in tags and tags["service"] == "parking_aisle":
|
||||
style += "5"
|
||||
else:
|
||||
style += "9"
|
||||
elif v == "cycleway":
|
||||
style += \
|
||||
f"1;stroke-dasharray:8,2;istroke-linecap:butt;" \
|
||||
f"stroke:#{self.color('cycle_color')}"
|
||||
elif v in ["footway", "pedestrian"]:
|
||||
if "area" in tags and tags["area"] == "yes":
|
||||
style += "1;stroke:none;fill:#DDDDDD"
|
||||
layer -= 55 # FIXME!
|
||||
else:
|
||||
style += \
|
||||
"1.5;stroke-dasharray:7,3;stroke-linecap:round;stroke:#"
|
||||
if "guide_strips" in tags and tags["guide_strips"] == "yes":
|
||||
style += self.color('guide_strips_color')
|
||||
else:
|
||||
style += self.color('foot_color')
|
||||
elif v == "steps":
|
||||
style += "5;stroke-dasharray:1.5,2;stroke-linecap:butt;" + \
|
||||
"stroke:#"
|
||||
if "conveying" in tags:
|
||||
style += "888888"
|
||||
else:
|
||||
style += self.color('foot_color')
|
||||
elif v == "path":
|
||||
style += "1;stroke-dasharray:5,5;stroke-linecap:butt;" + \
|
||||
"stroke:#" + self.color('foot_color')
|
||||
style += ";"
|
||||
self.ways.append(Way("way", nodes, path, style, layer + 42, 50))
|
||||
if "oneway" in tags and tags["oneway"] == "yes" or \
|
||||
"conveying" in tags and tags["conveying"] == "forward":
|
||||
for k in range(7):
|
||||
self.ways.append(Way(
|
||||
"way", nodes, path,
|
||||
f"fill:none;stroke:#EEEEEE;stroke-linecap:butt;"
|
||||
f"stroke-width:{7 - k};stroke-dasharray:{k},{40 - k};",
|
||||
layer + 43, 50))
|
||||
if "access" in tags and tags["access"] == "private":
|
||||
self.ways.append(Way(
|
||||
"way", nodes, path,
|
||||
f"fill:none;stroke:#{self.color('private_access_color')};"
|
||||
f"stroke-linecap:butt;stroke-width:10;stroke-dasharray:1,5;"
|
||||
f"opacity:0.4;", layer + 0.1, 50))
|
||||
|
||||
# Leisure
|
||||
|
||||
if "leisure" in tags:
|
||||
layer += 21
|
||||
if tags["leisure"] == "playground":
|
||||
style = f"fill:#{self.color('playground_color')};opacity:0.2;"
|
||||
# FIXME!!!!!!!!!!!!!!!!!!!!!
|
||||
# if nodes:
|
||||
# self.draw_point_shape("toy_horse", c[0], c[1], "444444")
|
||||
elif tags["leisure"] == "garden":
|
||||
style = f"fill:#{self.color('grass_color')};"
|
||||
elif tags["leisure"] == "pitch":
|
||||
style = f"fill:#{self.color('playground_color')};opacity:0.2;"
|
||||
elif tags["leisure"] == "park":
|
||||
return
|
||||
else:
|
||||
style = "fill:#FF0000;opacity:0.2;"
|
||||
self.ways.append(Way("way", nodes, path, style, layer, 50))
|
||||
|
||||
# Barrier
|
||||
|
||||
if "barrier" in tags:
|
||||
style = "fill:none;stroke:none;"
|
||||
layer += 40
|
||||
if tags["barrier"] == "hedge":
|
||||
style += \
|
||||
f"fill:none;stroke:#{self.color('wood_color')};" \
|
||||
f"stroke-width:4;"
|
||||
elif tags["barrier"] == "fense":
|
||||
style += "fill:none;stroke:#000000;stroke-width:1;opacity:0.4;"
|
||||
elif tags["barrier"] == "kerb":
|
||||
style += "fill:none;stroke:#000000;stroke-width:1;opacity:0.2;"
|
||||
else:
|
||||
style += "fill:none;stroke:#000000;stroke-width:1;opacity:0.3;"
|
||||
self.ways.append(Way("way", nodes, path, style, layer, 50))
|
||||
|
||||
# Border
|
||||
|
||||
if "border" in tags:
|
||||
style = "fill:none;stroke:none;"
|
||||
style += "fill:none;stroke:#FF0000;stroke-width:0.5;" + \
|
||||
"stroke-dahsarray:10,20;"
|
||||
self.ways.append(Way("way", nodes, path, style, layer, 50))
|
||||
if "area:highway" in tags:
|
||||
style = "fill:none;stroke:none;"
|
||||
if tags["area:highway"] == "yes":
|
||||
style += "fill:#FFFFFF;stroke:#DDDDDD;stroke-width:1;"
|
||||
self.ways.append(Way("way", nodes, path, style, layer, 50))
|
||||
|
||||
def construct_relations(self):
|
||||
def construct_relations(self) -> None:
|
||||
"""
|
||||
Construct Röntgen ways from OSM relations.
|
||||
"""
|
||||
|
@ -603,7 +335,7 @@ class Constructor:
|
|||
p += path + " "
|
||||
self.construct_way(None, tags, p)
|
||||
|
||||
def construct_nodes(self):
|
||||
def construct_nodes(self) -> None:
|
||||
"""
|
||||
Draw nodes.
|
||||
"""
|
||||
|
@ -618,33 +350,27 @@ class Constructor:
|
|||
|
||||
for node_id in s: # type: int
|
||||
node_number += 1
|
||||
ui.write_line(node_number, len(self.map_.node_map))
|
||||
ui.progress_bar(node_number, len(self.map_.node_map))
|
||||
node = self.map_.node_map[node_id]
|
||||
flung = self.flinger.fling(Geo(node.lat, node.lon))
|
||||
x = flung[0]
|
||||
y = flung[1]
|
||||
tags = node.tags
|
||||
|
||||
if not self.check_level(tags):
|
||||
continue
|
||||
|
||||
shapes, fill, processed = self.scheme.get_icon(tags)
|
||||
icon_set: IconSet = self.scheme.get_icon(tags)
|
||||
|
||||
if self.mode in ["time", "user-coloring"]:
|
||||
if not tags:
|
||||
continue
|
||||
shapes = ["small"]
|
||||
icon_set.icons = [[DEFAULT_SMALL_SHAPE_ID]]
|
||||
if self.mode == "user-coloring":
|
||||
fill = get_user_color(node.user, self.seed)
|
||||
icon_set.color = get_user_color(node.user, self.seed)
|
||||
if self.mode == "time":
|
||||
fill = get_time_color(node.timestamp)
|
||||
icon_set.color = get_time_color(node.timestamp)
|
||||
|
||||
if shapes == [] and tags != {}:
|
||||
shapes = [["no"]]
|
||||
self.nodes.append(Node(icon_set, tags, flung, None))
|
||||
|
||||
self.nodes.append(Node(
|
||||
shapes, tags, x, y, fill, None, processed))
|
||||
|
||||
ui.write_line(-1, len(self.map_.node_map))
|
||||
ui.progress_bar(-1, len(self.map_.node_map))
|
||||
|
||||
print("Nodes painted in " + str(datetime.now() - start_time) + ".")
|
||||
|
|
|
@ -5,11 +5,52 @@ Author: Sergey Vartanov (me@enzet.ru).
|
|||
"""
|
||||
import re
|
||||
import xml.dom.minidom
|
||||
|
||||
from typing import Dict
|
||||
|
||||
import numpy as np
|
||||
from svgwrite import Drawing
|
||||
|
||||
from roentgen import ui
|
||||
|
||||
DEFAULT_SHAPE_ID: str = "default"
|
||||
DEFAULT_SMALL_SHAPE_ID: str = "default_small"
|
||||
|
||||
GRID_STEP: int = 16
|
||||
|
||||
|
||||
class Icon:
|
||||
"""
|
||||
SVG icon path description.
|
||||
"""
|
||||
def __init__(self, path: str, offset: np.array, id_: str):
|
||||
"""
|
||||
:param path: SVG icon path
|
||||
:param offset: vector that should be used to shift the path
|
||||
:param id_: shape identifier
|
||||
"""
|
||||
self.path: str = path
|
||||
self.offset: np.array = offset
|
||||
self.id_: str = id_
|
||||
|
||||
def is_default(self) -> bool:
|
||||
"""
|
||||
Return true if icon is has a default shape that doesn't represent
|
||||
anything.
|
||||
"""
|
||||
return self.id_ in [DEFAULT_SHAPE_ID, DEFAULT_SMALL_SHAPE_ID]
|
||||
|
||||
def get_path(self, svg: Drawing, point: np.array):
|
||||
"""
|
||||
Draw icon into SVG file.
|
||||
|
||||
:param svg: SVG file to draw to
|
||||
:param point: icon position
|
||||
"""
|
||||
shift: np.array = self.offset + point
|
||||
|
||||
return svg.path(
|
||||
d=self.path, transform=f"translate({shift[0]},{shift[1]})")
|
||||
|
||||
|
||||
class IconExtractor:
|
||||
"""
|
||||
|
@ -22,7 +63,7 @@ class IconExtractor:
|
|||
:param svg_file_name: input SVG file name with icons. File may contain
|
||||
any other irrelevant graphics.
|
||||
"""
|
||||
self.icons: Dict[str, (str, float, float)] = {}
|
||||
self.icons: Dict[str, Icon] = {}
|
||||
|
||||
with open(svg_file_name) as input_file:
|
||||
content = xml.dom.minidom.parse(input_file)
|
||||
|
@ -38,35 +79,39 @@ class IconExtractor:
|
|||
|
||||
:param node: XML node that contains icon
|
||||
"""
|
||||
if node.nodeName == "path":
|
||||
if "id" in node.attributes.keys() and \
|
||||
"d" in node.attributes.keys() and \
|
||||
node.attributes["id"].value:
|
||||
path = node.attributes["d"].value
|
||||
m = re.match("[Mm] ([0-9.e-]*)[, ]([0-9.e-]*)", path)
|
||||
if not m:
|
||||
ui.error(f"invalid path: {path}")
|
||||
else:
|
||||
x = int(float(m.group(1)) / 16)
|
||||
y = int(float(m.group(2)) / 16)
|
||||
self.icons[node.attributes["id"].value] = \
|
||||
(node.attributes["d"].value, x, y)
|
||||
else:
|
||||
if node.nodeName != "path":
|
||||
for sub_node in node.childNodes:
|
||||
self.parse(sub_node)
|
||||
return
|
||||
|
||||
def get_path(self, id_: str) -> (str, float, float, bool):
|
||||
if "id" in node.attributes.keys() and \
|
||||
"d" in node.attributes.keys() and \
|
||||
node.attributes["id"].value:
|
||||
path = node.attributes["d"].value
|
||||
matcher = re.match("[Mm] ([0-9.e-]*)[, ]([0-9.e-]*)", path)
|
||||
if not matcher:
|
||||
ui.error(f"invalid path: {path}")
|
||||
return
|
||||
|
||||
def get_offset(value: float):
|
||||
""" Get negated icon offset from the origin. """
|
||||
return -int(value / GRID_STEP) * GRID_STEP - GRID_STEP / 2
|
||||
|
||||
point: np.array = np.array((
|
||||
get_offset(float(matcher.group(1))),
|
||||
get_offset(float(matcher.group(2)))))
|
||||
|
||||
id_: str = node.attributes["id"].value
|
||||
self.icons[id_] = Icon(node.attributes["d"].value, point, id_)
|
||||
|
||||
def get_path(self, id_: str) -> (Icon, bool):
|
||||
"""
|
||||
Get SVG path of the icon.
|
||||
|
||||
:param id_: string icon ID
|
||||
:param id_: string icon identifier
|
||||
"""
|
||||
if id_ in self.icons:
|
||||
return list(self.icons[id_]) + [True]
|
||||
else:
|
||||
if id_ == "no":
|
||||
return "M 4,4 L 4,10 10,10 10,4 z", 0, 0, False
|
||||
if id_ == "small":
|
||||
return "M 6,6 L 6,8 8,8 8,6 z", 0, 0, False
|
||||
ui.error(f"no such icon ID {id_}")
|
||||
return "M 4,4 L 4,10 10,10 10,4 z", 0, 0, False
|
||||
return self.icons[id_], True
|
||||
|
||||
ui.error(f"no such icon ID {id_}")
|
||||
return self.icons[DEFAULT_SHAPE_ID], False
|
||||
|
|
|
@ -1,98 +1,46 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Author: Sergey Vartanov (me@enzet.ru)
|
||||
"""
|
||||
|
||||
import math
|
||||
import numpy as np
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def get_ratio(maximum, minimum, ratio: float = 1):
|
||||
return (maximum[0] - minimum[0]) * ratio / (maximum[1] - minimum[1])
|
||||
|
||||
|
||||
class Flinger:
|
||||
def map_(
|
||||
value: float, current_min: float, current_max: float, target_min: float,
|
||||
target_max: float):
|
||||
"""
|
||||
Flinger. Coordinates repositioning.
|
||||
Map current value in bounds of current_min and current_max to bounds of
|
||||
target_min and target_max.
|
||||
"""
|
||||
def __init__(
|
||||
self, minimum, maximum, target_minimum=None, target_maximum=None,
|
||||
ratio=None):
|
||||
|
||||
self.minimum = minimum
|
||||
self.maximum = maximum
|
||||
|
||||
if not target_minimum:
|
||||
target_minimum = [0, 0]
|
||||
if not target_maximum:
|
||||
target_maximum = maximum - minimum
|
||||
|
||||
space = [0, 0]
|
||||
|
||||
if ratio:
|
||||
if ratio == "geo":
|
||||
ratio = math.sin(
|
||||
(90.0 - ((self.maximum[1] + self.minimum[1]) / 2.0))
|
||||
/ 180.0 * math.pi)
|
||||
|
||||
current_ratio = get_ratio(self.maximum, self.minimum, ratio)
|
||||
target_ratio = get_ratio(target_maximum, target_minimum)
|
||||
|
||||
if current_ratio >= target_ratio:
|
||||
n = (target_maximum[0] - target_minimum[0]) / \
|
||||
(maximum[0] - minimum[0]) / ratio
|
||||
space[1] = \
|
||||
((target_maximum[1] - target_minimum[1]) -
|
||||
(maximum[1] - minimum[1]) * n) / 2.0
|
||||
space[0] = 0
|
||||
else:
|
||||
n = (target_maximum[1] - target_minimum[1]) / \
|
||||
(maximum[1] - minimum[1])
|
||||
space[0] = \
|
||||
((target_maximum[0] - target_minimum[0]) -
|
||||
(maximum[0] - minimum[0]) * n) / 2.0
|
||||
space[1] = 0
|
||||
|
||||
target_minimum[0] += space
|
||||
target_maximum[0] += space
|
||||
|
||||
self.target_minimum = target_minimum
|
||||
self.target_maximum = target_maximum
|
||||
|
||||
def fling(self, current):
|
||||
"""
|
||||
Fling current point to the surface.
|
||||
|
||||
:param current: vector to fling
|
||||
"""
|
||||
x = map_(
|
||||
current[0], self.minimum[0], self.maximum[0],
|
||||
self.target_minimum[0], self.target_maximum[0])
|
||||
y = map_(
|
||||
current[1], self.minimum[1], self.maximum[1],
|
||||
self.target_minimum[1], self.target_maximum[1])
|
||||
return [x, y]
|
||||
return \
|
||||
target_min + (value - current_min) / (current_max - current_min) * \
|
||||
(target_max - target_min)
|
||||
|
||||
|
||||
class Geo:
|
||||
def __init__(self, lat, lon):
|
||||
self.lat = lat
|
||||
self.lon = lon
|
||||
def __init__(self, lat: float, lon: float):
|
||||
self.lat: float = lat
|
||||
self.lon: float = lon
|
||||
|
||||
def __getitem__(self, item):
|
||||
def __getitem__(self, item) -> Optional[float]:
|
||||
if item == 0:
|
||||
return self.lon
|
||||
if item == 1:
|
||||
return self.lat
|
||||
return None
|
||||
|
||||
def __add__(self, other):
|
||||
def __add__(self, other: "Geo") -> "Geo":
|
||||
return Geo(self.lat + other.lat, self.lon + other.lon)
|
||||
|
||||
def __sub__(self, other):
|
||||
def __sub__(self, other: "Geo") -> "Geo":
|
||||
return Geo(self.lat - other.lat, self.lon - other.lon)
|
||||
|
||||
def __repr__(self):
|
||||
def __repr__(self) -> str:
|
||||
return f"{self.lat}, {self.lon}"
|
||||
|
||||
|
||||
|
@ -157,15 +105,3 @@ class GeoFlinger:
|
|||
self.minimum.lat, self.maximum.lat,
|
||||
self.target_minimum[1], self.target_maximum[1])
|
||||
return [x, y]
|
||||
|
||||
|
||||
def map_(
|
||||
value: float, current_min: float, current_max: float, target_min: float,
|
||||
target_max: float):
|
||||
"""
|
||||
Map current value in bounds of current_min and current_max to bounds of
|
||||
target_min and target_max.
|
||||
"""
|
||||
return \
|
||||
target_min + (value - current_min) / (current_max - current_min) * \
|
||||
(target_max - target_min)
|
||||
|
|
|
@ -1,24 +1,15 @@
|
|||
"""
|
||||
Author: Sergey Vartanov (me@enzet.ru).
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import os
|
||||
import random
|
||||
import svgwrite
|
||||
import yaml
|
||||
|
||||
from roentgen import extract_icon
|
||||
from roentgen.extract_icon import Icon, IconExtractor
|
||||
|
||||
from typing import Any, Dict
|
||||
|
||||
|
||||
def draw_icon(
|
||||
svg, icon: Dict[str, Any], x: float, y: float,
|
||||
color: str = "444444"):
|
||||
|
||||
svg.add(svg.path(
|
||||
d=icon["path"], fill=f"#{color}", stroke="none",
|
||||
transform=f'translate({icon["x"] + x},{icon["y"] + y})'))
|
||||
from typing import List
|
||||
|
||||
|
||||
def draw_grid():
|
||||
|
@ -47,8 +38,7 @@ def draw_grid():
|
|||
|
||||
width: float = 24 * 16
|
||||
|
||||
x: float = step / 2
|
||||
y: float = step / 2
|
||||
point: np.array = np.array((step / 2, step / 2))
|
||||
|
||||
to_draw = []
|
||||
|
||||
|
@ -83,20 +73,19 @@ def draw_grid():
|
|||
|
||||
number: int = 0
|
||||
|
||||
icons = []
|
||||
icons: List[List[Icon]] = []
|
||||
|
||||
extractor = extract_icon.IconExtractor(icons_file_name)
|
||||
extractor: IconExtractor = IconExtractor(icons_file_name)
|
||||
|
||||
for icons_to_draw in to_draw:
|
||||
drawed = False
|
||||
icon_set = {"icons": []}
|
||||
for icon in icons_to_draw:
|
||||
path, xx, yy, _ = extractor.get_path(icon)
|
||||
icon_set["icons"].append({"path": path,
|
||||
"x": (- 8.0 - xx * 16),
|
||||
"y": (- 8.0 - yy * 16)})
|
||||
drawed = True
|
||||
if drawed:
|
||||
found: bool = False
|
||||
icon_set: List[Icon] = []
|
||||
for icon_id in icons_to_draw: # type: str
|
||||
icon, got = extractor.get_path(icon_id)
|
||||
assert got
|
||||
icon_set.append(icon)
|
||||
found = True
|
||||
if found:
|
||||
icons.append(icon_set)
|
||||
number += 1
|
||||
|
||||
|
@ -109,13 +98,16 @@ def draw_grid():
|
|||
for icon in icons:
|
||||
background_color, foreground_color = random.choice(icon_colors)
|
||||
svg.add(svg.rect(
|
||||
(x - 2 - 8, y - 2 - 8), (20, 20), fill=f"#{background_color}"))
|
||||
for i in icon["icons"]:
|
||||
draw_icon(svg, i, x, y, foreground_color)
|
||||
x += step
|
||||
if x > width - 8:
|
||||
x = step / 2
|
||||
y += step
|
||||
point - np.array((-10, -10)), (20, 20),
|
||||
fill=f"#{background_color}"))
|
||||
for i in icon: # type: Icon
|
||||
path = i.get_path(svg, point)
|
||||
path.update({"fill": f"#{foreground_color}"})
|
||||
svg.add(path)
|
||||
point += np.array((step, 0))
|
||||
if point[0] > width - 8:
|
||||
point[0] = step / 2
|
||||
point += np.array((0, step))
|
||||
height += step
|
||||
|
||||
print(f"Icons: {number}.")
|
||||
|
|
|
@ -7,19 +7,18 @@ import numpy as np
|
|||
import os
|
||||
import svgwrite
|
||||
import sys
|
||||
import yaml
|
||||
|
||||
from svgwrite.container import Group
|
||||
from svgwrite.path import Path
|
||||
from svgwrite.shapes import Circle, Rect
|
||||
from svgwrite.text import Text
|
||||
from typing import List
|
||||
from typing import Dict, List
|
||||
|
||||
from roentgen import extract_icon
|
||||
from roentgen import ui
|
||||
from roentgen.constructor import Constructor, get_path
|
||||
from roentgen.constructor import Constructor, get_path, Node, Way
|
||||
from roentgen.flinger import GeoFlinger, Geo
|
||||
from roentgen.grid import draw_grid
|
||||
from roentgen.extract_icon import Icon, IconExtractor
|
||||
from roentgen.osm_getter import get_osm
|
||||
from roentgen.osm_reader import Map, OSMReader
|
||||
from roentgen.scheme import Scheme
|
||||
|
@ -29,70 +28,97 @@ TAGS_FILE_NAME: str = "data/tags.yml"
|
|||
COLORS_FILE_NAME: str = "data/colors.yml"
|
||||
MISSING_TAGS_FILE_NAME: str = "missing_tags.yml"
|
||||
|
||||
AUTHOR_MODE = "user-coloring"
|
||||
CREATION_TIME_MODE = "time"
|
||||
|
||||
|
||||
class Painter:
|
||||
|
||||
"""
|
||||
Map drawing.
|
||||
"""
|
||||
def __init__(
|
||||
self, show_missing_tags, overlap, draw_nodes, mode, draw_captions,
|
||||
map_, flinger, svg: svgwrite.Drawing, icons, scheme: Scheme):
|
||||
self, show_missing_tags: bool, overlap: int, draw_nodes: bool,
|
||||
mode: str, draw_captions: bool, map_: Map, flinger: GeoFlinger,
|
||||
svg: svgwrite.Drawing, icon_extractor: IconExtractor,
|
||||
scheme: Scheme):
|
||||
|
||||
self.show_missing_tags = show_missing_tags
|
||||
self.overlap = overlap
|
||||
self.draw_nodes = draw_nodes
|
||||
self.mode = mode
|
||||
self.show_missing_tags: bool = show_missing_tags
|
||||
self.overlap: int = overlap
|
||||
self.draw_nodes: bool = draw_nodes
|
||||
self.mode: str = mode
|
||||
self.draw_captions = draw_captions
|
||||
|
||||
self.map_ = map_
|
||||
self.flinger = flinger
|
||||
self.map_: Map = map_
|
||||
self.flinger: GeoFlinger = flinger
|
||||
self.svg: svgwrite.Drawing = svg
|
||||
self.icons = icons
|
||||
self.icon_extractor = icon_extractor
|
||||
self.scheme: Scheme = scheme
|
||||
|
||||
def draw_shapes(self, shapes, points, x, y, fill, tags, processed):
|
||||
def draw_shapes(self, node: Node, points: List[List[float]]):
|
||||
"""
|
||||
Draw shapes for one node.
|
||||
"""
|
||||
if node.icon_set.is_default and not node.is_for_node:
|
||||
return
|
||||
|
||||
xxx = -(len(shapes) - 1) * 8
|
||||
left: float = -(len(node.icon_set.icons) - 1) * 8
|
||||
|
||||
if self.overlap != 0:
|
||||
for shape in shapes:
|
||||
for shape_ids in node.icon_set.icons:
|
||||
has_space = True
|
||||
for p in points[-1000:]:
|
||||
if x + xxx - self.overlap <= p[0] <= x + xxx + self.overlap and \
|
||||
y - self.overlap <= p[1] <= y + self.overlap:
|
||||
if node.point[0] + left - self.overlap <= p[0] \
|
||||
<= node.point[0] + left + self.overlap and \
|
||||
node.point[1] - self.overlap <= p[1] \
|
||||
<= node.point[1] + self.overlap:
|
||||
has_space = False
|
||||
break
|
||||
if has_space:
|
||||
self.draw_point_shape(shape, x + xxx, y, fill, tags=tags)
|
||||
points.append([x + xxx, y])
|
||||
xxx += 16
|
||||
self.draw_point_shape(
|
||||
shape_ids, (node.point[0] + left, node.point[1]),
|
||||
node.icon_set.color, tags=node.tags)
|
||||
points.append([node.point[0] + left, node.point[1]])
|
||||
left += 16
|
||||
else:
|
||||
for shape in shapes:
|
||||
self.draw_point_shape(shape, x + xxx, y, fill, tags=tags)
|
||||
xxx += 16
|
||||
|
||||
def draw_texts(self, shapes, points, x, y, fill, tags, processed):
|
||||
|
||||
if self.draw_captions == "no":
|
||||
return
|
||||
for shape_ids in node.icon_set.icons:
|
||||
self.draw_point_shape(
|
||||
shape_ids, (node.point[0] + left, node.point[1]),
|
||||
node.icon_set.color, tags=node.tags)
|
||||
left += 16
|
||||
|
||||
def draw_texts(self, node: Node):
|
||||
"""
|
||||
Draw all labels.
|
||||
"""
|
||||
text_y: float = 0
|
||||
|
||||
write_tags = self.construct_text(tags, processed)
|
||||
write_tags = self.construct_text(node.tags, node.icon_set.processed)
|
||||
|
||||
for text_struct in write_tags:
|
||||
fill = text_struct["fill"] if "fill" in text_struct else "444444"
|
||||
size = text_struct["size"] if "size" in text_struct else 10
|
||||
text_y += size + 1
|
||||
self.wr(text_struct["text"], x, y, fill, text_y, size=size)
|
||||
text = text_struct["text"]
|
||||
text = text.replace(""", '"')
|
||||
text = text.replace("&", '&')
|
||||
text = text[:26] + ("..." if len(text) > 26 else "")
|
||||
self.draw_text(
|
||||
text, (node.point[0], node.point[1] + text_y + 8),
|
||||
fill, size=size)
|
||||
|
||||
if self.show_missing_tags:
|
||||
for k in tags:
|
||||
if not self.no_draw(k) and k not in processed:
|
||||
text = k + ": " + tags[k]
|
||||
self.draw_text(text, x, float(y) + text_y + 18, "734A08")
|
||||
for tag in node.tags: # type: str
|
||||
if not self.scheme.is_no_drawable(tag) and \
|
||||
tag not in node.icon_set.processed:
|
||||
text = f"{tag}: {node.tags[tag]}"
|
||||
self.draw_text(
|
||||
text, (node.point[0], node.point[1] + text_y + 18),
|
||||
"734A08")
|
||||
text_y += 10
|
||||
|
||||
def draw_text(self, text: str, x, y, fill, size=10, out_fill="FFFFFF",
|
||||
out_opacity=1.0, out_fill_2=None, out_opacity_2=1.0):
|
||||
def draw_text(
|
||||
self, text: str, point, fill, size=10, out_fill="FFFFFF",
|
||||
out_opacity=1.0, out_fill_2=None, out_opacity_2=1.0):
|
||||
"""
|
||||
Drawing text.
|
||||
|
||||
|
@ -104,27 +130,24 @@ class Painter:
|
|||
"""
|
||||
if out_fill_2:
|
||||
self.svg.add(Text(
|
||||
text, (x, y), font_size=size, text_anchor="middle",
|
||||
text, point, font_size=size, text_anchor="middle",
|
||||
font_family="Roboto", fill=f"#{out_fill_2}",
|
||||
stroke_linejoin="round", stroke_width=5,
|
||||
stroke=f"#{out_fill_2}", opacity=out_opacity_2))
|
||||
if out_fill:
|
||||
self.svg.add(Text(
|
||||
text, (x, y), font_size=size, text_anchor="middle",
|
||||
text, point, font_size=size, text_anchor="middle",
|
||||
font_family="Roboto", fill=f"#{out_fill}",
|
||||
stroke_linejoin="round", stroke_width=3,
|
||||
stroke=f"#{out_fill}", opacity=out_opacity))
|
||||
self.svg.add(Text(
|
||||
text, (x, y), font_size=size, text_anchor="middle",
|
||||
text, point, font_size=size, text_anchor="middle",
|
||||
font_family="Roboto", fill=f"#{fill}"))
|
||||
|
||||
def wr(self, text, x, y, fill, text_y, size=10):
|
||||
text = text[:26] + ("..." if len(text) > 26 else "")
|
||||
self.draw_text(text, x, float(y) + text_y + 8, fill, size=size)
|
||||
|
||||
def construct_text(self, tags, processed):
|
||||
for key in tags:
|
||||
tags[key] = tags[key].replace(""", '"')
|
||||
"""
|
||||
Construct labels for not processed tags.
|
||||
"""
|
||||
texts = []
|
||||
address: List[str] = []
|
||||
name = None
|
||||
|
@ -207,8 +230,7 @@ class Painter:
|
|||
texts.append({"text": tags[k], "fill": "444444"})
|
||||
tags.pop(k)
|
||||
for tag in tags:
|
||||
if self.to_write(tag) and not (tag in processed):
|
||||
# texts.append({"text": tag + ": " + tags[tag]})
|
||||
if self.scheme.is_writable(tag) and not (tag in processed):
|
||||
texts.append({"text": tags[tag]})
|
||||
return texts
|
||||
|
||||
|
@ -250,21 +272,27 @@ class Painter:
|
|||
pass
|
||||
|
||||
def draw(self, nodes, ways, points):
|
||||
|
||||
"""
|
||||
Draw map.
|
||||
"""
|
||||
ways = sorted(ways, key=lambda x: x.layer)
|
||||
for way in ways:
|
||||
if way.kind == "way":
|
||||
if way.nodes:
|
||||
path = get_path(way.nodes, [0, 0], self.map_, self.flinger)
|
||||
self.svg.add(Path(d=path, style=way.style))
|
||||
p = Path(d=path)
|
||||
p.update(way.style)
|
||||
self.svg.add(p)
|
||||
else:
|
||||
self.svg.add(Path(d=way.path, style=way.style))
|
||||
p = Path(d=way.path)
|
||||
p.update(way.style)
|
||||
self.svg.add(p)
|
||||
|
||||
# Building shade
|
||||
|
||||
building_shade = Group(opacity=0.1)
|
||||
|
||||
for way in ways:
|
||||
for way in ways: # type: Way
|
||||
if way.kind != "building" or not way.nodes:
|
||||
continue
|
||||
shift = [-5, 5]
|
||||
|
@ -290,7 +318,7 @@ class Painter:
|
|||
|
||||
# Building roof
|
||||
|
||||
for way in ways:
|
||||
for way in ways: # type: Way
|
||||
if way.kind != "building":
|
||||
continue
|
||||
if way.nodes:
|
||||
|
@ -298,9 +326,13 @@ class Painter:
|
|||
if way.levels:
|
||||
shift = [0 * way.levels, min(-3, -1 * way.levels)]
|
||||
path = get_path(way.nodes, shift, self.map_, self.flinger)
|
||||
self.svg.add(Path(d=path, style=way.style, opacity=1))
|
||||
p = Path(d=path, opacity=1)
|
||||
p.update(way.style)
|
||||
self.svg.add(p)
|
||||
else:
|
||||
self.svg.add(Path(d=way.path, style=way.style, opacity=1))
|
||||
p = Path(d=way.path, opacity=1)
|
||||
p.update(way.style)
|
||||
self.svg.add(p)
|
||||
|
||||
# Trees
|
||||
|
||||
|
@ -310,58 +342,59 @@ class Painter:
|
|||
"diameter_crown" in node.tags):
|
||||
continue
|
||||
self.svg.add(Circle(
|
||||
(float(node.x), float(node.y)),
|
||||
float(node.tags["diameter_crown"]) * 1.2,
|
||||
node.point, float(node.tags["diameter_crown"]) * 1.2,
|
||||
fill="#688C44", stroke="#688C44", opacity=0.3))
|
||||
|
||||
# All other nodes
|
||||
|
||||
nodes = sorted(nodes, key=lambda x: x.layer)
|
||||
for node in nodes:
|
||||
for node in nodes: # type: Node
|
||||
if "natural" in node.tags and \
|
||||
node.tags["natural"] == "tree" and \
|
||||
"diameter_crown" in node.tags:
|
||||
continue
|
||||
self.draw_shapes(
|
||||
node.shapes, points, node.x, node.y, node.color, node.tags,
|
||||
node.processed)
|
||||
self.draw_shapes(node, points)
|
||||
|
||||
for node in nodes:
|
||||
if self.mode not in ["time", "user-coloring"]:
|
||||
self.draw_texts(
|
||||
node.shapes, points, node.x, node.y, node.color,
|
||||
node.tags, node.processed)
|
||||
if self.draw_captions == "no":
|
||||
return
|
||||
|
||||
def draw_point_shape(self, name, x, y, fill, tags=None):
|
||||
if not isinstance(name, list):
|
||||
name = [name]
|
||||
if self.mode not in ["time", "user-coloring"]:
|
||||
for one_name in name:
|
||||
shape, xx, yy, _ = self.icons.get_path(one_name)
|
||||
self.draw_point_outline(
|
||||
shape, x, y, fill, mode=self.mode, size=16, xx=xx, yy=yy)
|
||||
for one_name in name:
|
||||
shape, xx, yy, _ = self.icons.get_path(one_name)
|
||||
self.draw_point(shape, x, y, fill, size=16, xx=xx, yy=yy, tags=tags)
|
||||
for node in nodes: # type: Node
|
||||
if self.mode not in [CREATION_TIME_MODE, AUTHOR_MODE]:
|
||||
self.draw_texts(node)
|
||||
|
||||
def draw_point(self, shape, x, y, fill, size=16, xx=0, yy=0, tags=None):
|
||||
x = int(float(x))
|
||||
y = int(float(y))
|
||||
path = self.svg.path(
|
||||
d=shape, fill=f"#{fill}", fill_opacity=1,
|
||||
transform=f"translate({x - size / 2.0 - xx * 16},"
|
||||
f"{y - size / 2.0 - yy * 16})")
|
||||
path.set_desc(title="\n".join(map(lambda x: x + ": " + tags[x], tags)))
|
||||
def draw_point_shape(self, shape_ids: List[str], point, fill, tags=None):
|
||||
"""
|
||||
Draw one icon.
|
||||
"""
|
||||
if self.mode not in [CREATION_TIME_MODE, AUTHOR_MODE]:
|
||||
for shape_id in shape_ids: # type: str
|
||||
icon, _ = self.icon_extractor.get_path(shape_id)
|
||||
self.draw_point_outline(icon, point, fill, mode=self.mode)
|
||||
for shape_id in shape_ids: # type: str
|
||||
icon, _ = self.icon_extractor.get_path(shape_id)
|
||||
self.draw_point(icon, point, fill, tags=tags)
|
||||
|
||||
def draw_point(
|
||||
self, icon: Icon, point: (float, float), fill: str,
|
||||
tags: Dict[str, str] = None) -> None:
|
||||
|
||||
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.update({"fill": f"#{fill}"})
|
||||
path.set_desc(title=title)
|
||||
self.svg.add(path)
|
||||
|
||||
def draw_point_outline(
|
||||
self, shape, x, y, fill, mode="default", size=16, xx=0, yy=0):
|
||||
x = int(float(x))
|
||||
y = int(float(y))
|
||||
self, icon: Icon, point, fill, mode="default", size=16):
|
||||
|
||||
point = np.array(list(map(lambda x: int(x), point)))
|
||||
|
||||
opacity = 0.5
|
||||
stroke_width = 2.2
|
||||
outline_fill = self.scheme.get_color("outline_color")
|
||||
if mode not in ["user-coloring", "time"]:
|
||||
if mode not in [AUTHOR_MODE, CREATION_TIME_MODE]:
|
||||
r = int(fill[0:2], 16)
|
||||
g = int(fill[2:4], 16)
|
||||
b = int(fill[4:6], 16)
|
||||
|
@ -369,12 +402,13 @@ class Painter:
|
|||
if Y > 200:
|
||||
outline_fill = "000000"
|
||||
opacity = 0.7
|
||||
self.svg.add(self.svg.path(
|
||||
d=shape, fill=f"#{outline_fill}", opacity=opacity,
|
||||
stroke=f"#{outline_fill}", stroke_width=stroke_width,
|
||||
stroke_linejoin="round",
|
||||
transform=f"translate({x - size / 2.0 - xx * 16},"
|
||||
f"{y - size / 2.0 - yy * 16})"))
|
||||
|
||||
path = icon.get_path(self.svg, point)
|
||||
path.update({
|
||||
"fill": f"#{outline_fill}", "opacity": opacity,
|
||||
"stroke": f"#{outline_fill}", "stroke-width": stroke_width,
|
||||
"stroke-linejoin": "round"})
|
||||
self.svg.add(path)
|
||||
|
||||
|
||||
def check_level_number(tags, level):
|
||||
|
@ -395,6 +429,14 @@ def check_level_overground(tags):
|
|||
for level in levels:
|
||||
if level <= 0:
|
||||
return False
|
||||
if "layer" in tags:
|
||||
levels = \
|
||||
map(lambda x: float(x), tags["layer"].replace(",", ".").split(";"))
|
||||
for level in levels:
|
||||
if level <= 0:
|
||||
return False
|
||||
if "parking" in tags and tags["parking"] == "underground":
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
|
@ -410,7 +452,7 @@ def main():
|
|||
sys.exit(1)
|
||||
|
||||
background_color = "#EEEEEE"
|
||||
if options.mode in ["user-coloring", "time"]:
|
||||
if options.mode in [AUTHOR_MODE, CREATION_TIME_MODE]:
|
||||
background_color = "#111111"
|
||||
|
||||
if options.input_file_name:
|
||||
|
@ -426,7 +468,7 @@ def main():
|
|||
|
||||
full = False # Full keys getting
|
||||
|
||||
if options.mode in ["user-coloring", "time"]:
|
||||
if options.mode in [AUTHOR_MODE, CREATION_TIME_MODE]:
|
||||
full = True
|
||||
|
||||
osm_reader = OSMReader()
|
||||
|
@ -460,7 +502,7 @@ def main():
|
|||
|
||||
flinger = GeoFlinger(min1, max1, [0, 0], [w, h])
|
||||
|
||||
icons = extract_icon.IconExtractor(ICONS_FILE_NAME)
|
||||
icon_extractor = IconExtractor(ICONS_FILE_NAME)
|
||||
|
||||
def check_level(x):
|
||||
""" Draw objects on all levels. """
|
||||
|
@ -489,7 +531,7 @@ def main():
|
|||
show_missing_tags=options.show_missing_tags, overlap=options.overlap,
|
||||
draw_nodes=options.draw_nodes, mode=options.mode,
|
||||
draw_captions=options.draw_captions,
|
||||
map_=map_, flinger=flinger, svg=svg, icons=icons,
|
||||
map_=map_, flinger=flinger, svg=svg, icon_extractor=icon_extractor,
|
||||
scheme=scheme)
|
||||
painter.draw(constructor.nodes, constructor.ways, points)
|
||||
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
"""
|
||||
Getting OpenStreetMap data from the web.
|
||||
|
||||
Author: Sergey Vartanov (me@enzet.ru).
|
||||
"""
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
|
|
|
@ -83,9 +83,15 @@ class OSMWay:
|
|||
return self
|
||||
|
||||
def is_cycle(self) -> bool:
|
||||
"""
|
||||
Is way a cycle way or an area boundary.
|
||||
"""
|
||||
return self.nodes[0] == self.nodes[-1]
|
||||
|
||||
def try_to_glue(self, other: "OSMWay"):
|
||||
"""
|
||||
Create new combined way if ways share endpoints.
|
||||
"""
|
||||
if self.nodes[0] == other.nodes[0]:
|
||||
return OSMWay(nodes=list(reversed(other.nodes[1:])) + self.nodes)
|
||||
elif self.nodes[0] == other.nodes[-1]:
|
||||
|
@ -142,6 +148,9 @@ def get_value(key: str, text: str):
|
|||
|
||||
|
||||
class Map:
|
||||
"""
|
||||
The whole OpenStreetMap information about nodes, ways, and relations.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.node_map: Dict[int, OSMNode] = {}
|
||||
self.way_map: Dict[int, OSMWay] = {}
|
||||
|
@ -162,19 +171,18 @@ class OSMReader:
|
|||
"""
|
||||
Parse OSM XML representation.
|
||||
"""
|
||||
print(f"Line number counting for {file_name}...")
|
||||
with open(file_name) as f:
|
||||
for lines_number, _ in enumerate(f):
|
||||
pass
|
||||
print("Done.")
|
||||
lines_number: int = sum(1 for _ in open(file_name))
|
||||
|
||||
print(f"Parsing OSM file {file_name}...")
|
||||
input_file = open(file_name)
|
||||
line = input_file.readline()
|
||||
line_number = 0
|
||||
|
||||
element = None
|
||||
|
||||
while line != "":
|
||||
line_number += 1
|
||||
ui.write_line(line_number, lines_number)
|
||||
ui.progress_bar(line_number, lines_number)
|
||||
|
||||
# Node parsing.
|
||||
|
||||
|
@ -237,6 +245,6 @@ class OSMReader:
|
|||
line = input_file.readline()
|
||||
input_file.close()
|
||||
|
||||
ui.write_line(-1, lines_number) # Complete progress bar.
|
||||
ui.progress_bar(-1, lines_number) # Complete progress bar.
|
||||
|
||||
return self.map_
|
||||
|
|
|
@ -1,11 +1,37 @@
|
|||
"""
|
||||
Röntgen drawing scheme.
|
||||
|
||||
Author: Sergey Vartanov (me@enzet.ru).
|
||||
"""
|
||||
import copy
|
||||
import yaml
|
||||
|
||||
from typing import Any, Dict, List
|
||||
from typing import Any, Dict, List, Optional, Set
|
||||
|
||||
from roentgen.extract_icon import DEFAULT_SHAPE_ID
|
||||
|
||||
DEFAULT_COLOR: str = "444444"
|
||||
|
||||
|
||||
class IconSet:
|
||||
"""
|
||||
Node representation: icons and color.
|
||||
"""
|
||||
def __init__(
|
||||
self, icons: List[List[str]], color: str, processed: Set[str],
|
||||
is_default: bool):
|
||||
"""
|
||||
:param icons: list of lists of shape identifiers
|
||||
:param color: fill color of all icons
|
||||
:param processed: tag keys that were processed to create icon set (other
|
||||
tag keys should be displayed by text or ignored)
|
||||
"""
|
||||
self.icons: List[List[str]] = icons
|
||||
self.color: str = color
|
||||
self.processed: Set[str] = processed
|
||||
self.is_default = is_default
|
||||
|
||||
|
||||
class Scheme:
|
||||
"""
|
||||
Map style.
|
||||
|
@ -22,6 +48,7 @@ class Scheme:
|
|||
yaml.load(open(file_name).read(), Loader=yaml.FullLoader)
|
||||
|
||||
self.tags: List[Dict[str, Any]] = content["tags"]
|
||||
self.ways: List[Dict[str, Any]] = content["ways"]
|
||||
|
||||
self.colors: Dict[str, str] = content["colors"]
|
||||
w3c_colors: Dict[str, str] = \
|
||||
|
@ -34,7 +61,8 @@ class Scheme:
|
|||
self.tags_to_skip: List[str] = content["tags_to_skip"]
|
||||
self.prefix_to_skip: List[str] = content["prefix_to_skip"]
|
||||
|
||||
self.cache = {}
|
||||
# Storage for created icon sets.
|
||||
self.cache: Dict[str, IconSet] = {}
|
||||
|
||||
def get_color(self, color: str) -> str:
|
||||
"""
|
||||
|
@ -81,16 +109,24 @@ class Scheme:
|
|||
return True
|
||||
return False
|
||||
|
||||
def get_icon(self, tags: Dict[str, Any]):
|
||||
def get_icon(self, tags: Dict[str, Any]) -> IconSet:
|
||||
"""
|
||||
Construct icon set.
|
||||
|
||||
:param tags: OpenStreetMap element tags dictionary
|
||||
"""
|
||||
tags_hash: str = \
|
||||
",".join(tags.keys()) + ":" + \
|
||||
",".join(map(lambda x: str(x), tags.values()))
|
||||
|
||||
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 = []
|
||||
|
||||
main_icon: Optional[List[str]] = None
|
||||
extra_icons: List[List[str]] = []
|
||||
processed = set()
|
||||
fill = DEFAULT_COLOR
|
||||
|
||||
for matcher in self.tags:
|
||||
matched = True
|
||||
for key in matcher["tags"]:
|
||||
|
@ -118,7 +154,7 @@ class Scheme:
|
|||
for key in matcher["tags"].keys():
|
||||
processed.add(key)
|
||||
if "add_icon" in matcher:
|
||||
extra_icons += matcher["add_icon"]
|
||||
extra_icons += [matcher["add_icon"]]
|
||||
for key in matcher["tags"].keys():
|
||||
processed.add(key)
|
||||
if "color" in matcher:
|
||||
|
@ -126,17 +162,24 @@ class Scheme:
|
|||
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)
|
||||
for tag_key in tags: # type: str
|
||||
if tag_key in ["color", "colour"] or tag_key.endswith(":color") or \
|
||||
tag_key.endswith(":colour"):
|
||||
fill = self.get_color(tags[tag_key])
|
||||
processed.add(tag_key)
|
||||
|
||||
if main_icon:
|
||||
returned = [main_icon] + extra_icons, fill, processed
|
||||
result_set: List[List[str]] = [main_icon] + extra_icons
|
||||
else:
|
||||
returned = extra_icons, fill, processed
|
||||
result_set: List[List[str]] = extra_icons
|
||||
|
||||
is_default: bool = False
|
||||
if not result_set and tags:
|
||||
result_set = [[DEFAULT_SHAPE_ID]]
|
||||
is_default = True
|
||||
|
||||
returned: IconSet = IconSet(result_set, fill, processed, is_default)
|
||||
|
||||
self.cache[tags_hash] = returned
|
||||
|
||||
return returned
|
||||
|
||||
|
|
|
@ -4,7 +4,10 @@ Author: Sergey Vartanov (me@enzet.ru).
|
|||
import argparse
|
||||
import sys
|
||||
|
||||
from typing import Optional
|
||||
from typing import List, Optional
|
||||
|
||||
BOXES: List[str] = [" ", "▏", "▎", "▍", "▌", "▋", "▊", "▉"]
|
||||
BOXES_LENGTH: int = len(BOXES)
|
||||
|
||||
|
||||
def parse_options(args):
|
||||
|
@ -43,24 +46,44 @@ def parse_options(args):
|
|||
action="store_false",
|
||||
default=True)
|
||||
parser.add_argument(
|
||||
"-nw", "--no-draw-ways", dest="draw_ways", action="store_false",
|
||||
"-nw", "--no-draw-ways",
|
||||
dest="draw_ways",
|
||||
action="store_false",
|
||||
default=True)
|
||||
parser.add_argument(
|
||||
"--captions", "--no-draw-captions", dest="draw_captions",
|
||||
"--captions", "--no-draw-captions",
|
||||
dest="draw_captions",
|
||||
default="main")
|
||||
parser.add_argument(
|
||||
"--show-missing-tags", dest="show_missing_tags", action="store_true")
|
||||
"--show-missing-tags",
|
||||
dest="show_missing_tags",
|
||||
action="store_true")
|
||||
parser.add_argument(
|
||||
"--no-show-missing-tags", dest="show_missing_tags",
|
||||
"--no-show-missing-tags",
|
||||
dest="show_missing_tags",
|
||||
action="store_false")
|
||||
parser.add_argument("--overlap", dest="overlap", default=12, type=int)
|
||||
parser.add_argument(
|
||||
"--show-index", dest="show_index", action="store_true")
|
||||
"--overlap",
|
||||
dest="overlap",
|
||||
default=12,
|
||||
type=int)
|
||||
parser.add_argument(
|
||||
"--no-show-index", dest="show_index", action="store_false")
|
||||
parser.add_argument("--mode", default="normal")
|
||||
parser.add_argument("--seed", default="")
|
||||
parser.add_argument("--level", default=None)
|
||||
"--show-index",
|
||||
dest="show_index",
|
||||
action="store_true")
|
||||
parser.add_argument(
|
||||
"--no-show-index",
|
||||
dest="show_index",
|
||||
action="store_false")
|
||||
parser.add_argument(
|
||||
"--mode",
|
||||
default="normal")
|
||||
parser.add_argument(
|
||||
"--seed",
|
||||
default="")
|
||||
parser.add_argument(
|
||||
"--level",
|
||||
default=None)
|
||||
|
||||
arguments = parser.parse_args(args[1:])
|
||||
|
||||
|
@ -70,20 +93,27 @@ def parse_options(args):
|
|||
return arguments
|
||||
|
||||
|
||||
def write_line(number, total):
|
||||
length = 20
|
||||
parts = length * 8
|
||||
boxes = [" ", "▏", "▎", "▍", "▌", "▋", "▊", "▉"]
|
||||
def progress_bar(
|
||||
number: int, total: int, length: int = 20, step: int = 1000) -> None:
|
||||
"""
|
||||
Draw progress bar using Unicode symbols.
|
||||
|
||||
:param number: current value
|
||||
:param total: maximum value
|
||||
:param length: progress bar length.
|
||||
:param step: frequency of progress bar updating (assuming that numbers go
|
||||
subsequently)
|
||||
"""
|
||||
if number == -1:
|
||||
print("%3s" % "100" + " % █" + (length * "█") + "█")
|
||||
elif number % 1000 == 0:
|
||||
p = number / float(total)
|
||||
l = int(p * parts)
|
||||
fl = int(l / 8)
|
||||
pr = int(l - fl * 8)
|
||||
print(("%3s" % str(int(p * 1000) / 10)) + " % █" + (fl * "█") +
|
||||
boxes[pr] + ((length - fl - 1) * " ") + "█")
|
||||
print(f"100 % {length * '█'}▏")
|
||||
elif number % step == 0:
|
||||
ratio: float = number / total
|
||||
parts: int = int(ratio * length * BOXES_LENGTH)
|
||||
fill_length: int = int(parts / BOXES_LENGTH)
|
||||
box: str = BOXES[int(parts - fill_length * BOXES_LENGTH)]
|
||||
print(
|
||||
f"{str(int(int(ratio * 1000) / 10)):>3} % {fill_length * '█'}{box}"
|
||||
f"{int(length - fill_length - 1) * ' '}▏")
|
||||
sys.stdout.write("\033[F")
|
||||
|
||||
|
||||
|
|
5
run.py
|
@ -1,3 +1,8 @@
|
|||
"""
|
||||
Röntgen entry point.
|
||||
|
||||
Author: Sergey Vartanov (me@enzet.ru).
|
||||
"""
|
||||
from roentgen.mapper import main
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
44
test.py
|
@ -6,8 +6,8 @@ import os
|
|||
import random
|
||||
import yaml
|
||||
|
||||
from roentgen import extract_icon
|
||||
from roentgen.flinger import map_
|
||||
from roentgen.grid import draw_grid
|
||||
|
||||
|
||||
def test_flinger_map():
|
||||
|
@ -15,44 +15,4 @@ def test_flinger_map():
|
|||
|
||||
|
||||
def test_icons():
|
||||
tags_file_name = 'data/tags.yml'
|
||||
icons_file_name = 'icons/icons.svg'
|
||||
|
||||
scheme = yaml.load(open(tags_file_name))
|
||||
|
||||
extracter = extract_icon.IconExtractor(icons_file_name)
|
||||
|
||||
to_draw = []
|
||||
|
||||
for element in scheme['tags']:
|
||||
if 'icon' in element:
|
||||
if not (set(element['icon']) in to_draw):
|
||||
to_draw.append(set(element['icon']))
|
||||
if 'add_icon' in element:
|
||||
if not (set(element['add_icon']) in to_draw):
|
||||
to_draw.append(set(element['add_icon']))
|
||||
if 'over_icon' in element:
|
||||
with_icons = []
|
||||
if 'under_icon' in element:
|
||||
for icon in element['under_icon']:
|
||||
if not (set([icon] + element['over_icon']) in to_draw):
|
||||
to_draw.append(set([icon] + element['over_icon']))
|
||||
if 'under_icon' in element and 'with_icon' in element:
|
||||
for icon in element['under_icon']:
|
||||
for icon2 in element['with_icon']:
|
||||
if not (set([icon] + [icon2] + element['over_icon']) in to_draw):
|
||||
to_draw.append(set([icon] + [icon2] + element['over_icon']))
|
||||
for icon2 in element['with_icon']:
|
||||
for icon3 in element['with_icon']:
|
||||
if icon2 != icon3 and icon2 != icon and icon3 != icon:
|
||||
if not (set([icon] + [icon2] + [icon3] + element['over_icon']) in to_draw):
|
||||
to_draw.append(set([icon] + [icon2] + [icon3] + element['over_icon']))
|
||||
|
||||
for icons_to_draw in to_draw:
|
||||
icon_set = {'icons': []}
|
||||
for icon in icons_to_draw:
|
||||
path, xx, yy, is_shape = extracter.get_path(icon)
|
||||
assert is_shape, icon
|
||||
icon_set['icons'].append({'path': path,
|
||||
'x': (- 8.0 - xx * 16),
|
||||
'y': (- 8.0 - yy * 16)})
|
||||
draw_grid()
|
||||
|
|