mirror of
https://github.com/enzet/map-machine.git
synced 2025-04-30 18:57:49 +02:00
Closes #17: use svgwrite.
This commit is contained in:
parent
715ffcacb1
commit
f3a9e149b4
6 changed files with 118 additions and 384 deletions
|
@ -1,2 +1,4 @@
|
||||||
|
svgwrite
|
||||||
|
numpy>=1.18.1
|
||||||
PyYAML>=4.2b1
|
PyYAML>=4.2b1
|
||||||
urllib3>=1.25.6
|
urllib3>=1.25.6
|
||||||
|
|
|
@ -4,22 +4,21 @@ Author: Sergey Vartanov (me@enzet.ru).
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
|
import svgwrite
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
from roentgen import extract_icon
|
from roentgen import extract_icon
|
||||||
from roentgen import svg
|
|
||||||
|
|
||||||
from typing import Any, Dict
|
from typing import Any, Dict
|
||||||
|
|
||||||
|
|
||||||
def draw_icon(
|
def draw_icon(
|
||||||
output_file, icon: Dict[str, Any], x: float, y: float,
|
svg, icon: Dict[str, Any], x: float, y: float,
|
||||||
color: str = "444444"):
|
color: str = "444444"):
|
||||||
|
|
||||||
output_file.write(
|
svg.add(svg.path(
|
||||||
f'<path d="{icon["path"]}" '
|
d=icon["path"], fill=f"#{color}", stroke="none",
|
||||||
f'style="fill:#{color};stroke:none;" '
|
transform=f'translate({icon["x"] + x},{icon["y"] + y})'))
|
||||||
f'transform="translate({icon["x"] + x},{icon["y"] + y})" />\n')
|
|
||||||
|
|
||||||
|
|
||||||
def draw_grid():
|
def draw_grid():
|
||||||
|
@ -103,16 +102,16 @@ def draw_grid():
|
||||||
|
|
||||||
height = int(number / (width / step) + 1) * step
|
height = int(number / (width / step) + 1) * step
|
||||||
|
|
||||||
output_file = svg.SVG(open(icon_grid_file_name, "w+"))
|
svg = svgwrite.Drawing(icon_grid_file_name, (width, height))
|
||||||
output_file.begin(width, height)
|
|
||||||
|
|
||||||
output_file.rect(0, 0, width, height, color="FFFFFF")
|
svg.add(svg.rect((0, 0), (width, height), fill="#FFFFFF"))
|
||||||
|
|
||||||
for icon in icons:
|
for icon in icons:
|
||||||
background_color, foreground_color = random.choice(icon_colors)
|
background_color, foreground_color = random.choice(icon_colors)
|
||||||
output_file.rect(x - 2 - 8, y - 2 - 8, 20, 20, color=background_color)
|
svg.add(svg.rect(
|
||||||
|
(x - 2 - 8, y - 2 - 8), (20, 20), fill=f"#{background_color}"))
|
||||||
for i in icon["icons"]:
|
for i in icon["icons"]:
|
||||||
draw_icon(output_file, i, x, y, foreground_color)
|
draw_icon(svg, i, x, y, foreground_color)
|
||||||
x += step
|
x += step
|
||||||
if x > width - 8:
|
if x > width - 8:
|
||||||
x = step / 2
|
x = step / 2
|
||||||
|
@ -121,4 +120,4 @@ def draw_grid():
|
||||||
|
|
||||||
print(f"Icons: {number}.")
|
print(f"Icons: {number}.")
|
||||||
|
|
||||||
output_file.end()
|
svg.write(open(icon_grid_file_name, "w"))
|
||||||
|
|
|
@ -3,23 +3,25 @@ Simple OpenStreetMap renderer.
|
||||||
|
|
||||||
Author: Sergey Vartanov (me@enzet.ru).
|
Author: Sergey Vartanov (me@enzet.ru).
|
||||||
"""
|
"""
|
||||||
|
import numpy as np
|
||||||
import os
|
import os
|
||||||
import random
|
import svgwrite
|
||||||
import sys
|
import sys
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
import numpy as np
|
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 roentgen import extract_icon
|
from roentgen import extract_icon
|
||||||
from roentgen import ui
|
from roentgen import ui
|
||||||
from roentgen import svg
|
|
||||||
from roentgen.constructor import Constructor, get_path
|
from roentgen.constructor import Constructor, get_path
|
||||||
from roentgen.flinger import GeoFlinger, Geo
|
from roentgen.flinger import GeoFlinger, Geo
|
||||||
from roentgen.osm_reader import OSMReader
|
|
||||||
from roentgen.osm_getter import get_osm
|
|
||||||
from roentgen.grid import draw_grid
|
from roentgen.grid import draw_grid
|
||||||
|
from roentgen.osm_getter import get_osm
|
||||||
from typing import List
|
from roentgen.osm_reader import Map, OSMReader
|
||||||
|
|
||||||
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"
|
||||||
|
@ -31,7 +33,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, output_file, icons, scheme):
|
map_, flinger, svg: svgwrite.Drawing, icons, scheme):
|
||||||
|
|
||||||
self.show_missing_tags = show_missing_tags
|
self.show_missing_tags = show_missing_tags
|
||||||
self.overlap = overlap
|
self.overlap = overlap
|
||||||
|
@ -41,16 +43,10 @@ class Painter:
|
||||||
|
|
||||||
self.map_ = map_
|
self.map_ = map_
|
||||||
self.flinger = flinger
|
self.flinger = flinger
|
||||||
self.output_file = output_file
|
self.svg: svgwrite.Drawing = svg
|
||||||
self.icons = icons
|
self.icons = icons
|
||||||
self.scheme = scheme
|
self.scheme = scheme
|
||||||
|
|
||||||
def draw_raw_nodes(self):
|
|
||||||
for node_id in self.map_.node_map:
|
|
||||||
node = self.map_.node_map[node_id]
|
|
||||||
flung = self.flinger.fling(node)
|
|
||||||
self.output_file.circle(flung[0], flung[1], 0.2, color="FFFFFF")
|
|
||||||
|
|
||||||
def no_draw(self, key):
|
def no_draw(self, key):
|
||||||
if key in self.scheme["tags_to_write"] or \
|
if key in self.scheme["tags_to_write"] or \
|
||||||
key in self.scheme["tags_to_skip"]:
|
key in self.scheme["tags_to_skip"]:
|
||||||
|
@ -125,27 +121,21 @@ class Painter:
|
||||||
#------#
|
#------#
|
||||||
######
|
######
|
||||||
"""
|
"""
|
||||||
text = text.replace("&", "and")
|
|
||||||
if out_fill_2:
|
if out_fill_2:
|
||||||
self.output_file.write(
|
self.svg.add(Text(
|
||||||
f'<text x="{x}" y="{y}" style="font-size:' +
|
text, (x, y), font_size=size, text_anchor="middle",
|
||||||
str(
|
font_family="Roboto", fill=f"#{out_fill_2}",
|
||||||
size) + ";text-anchor:middle;font-family:Roboto;fill:#" +
|
stroke_linejoin="round", stroke_width=5,
|
||||||
out_fill_2 + ";stroke-linejoin:round;stroke-width:5;stroke:#" +
|
stroke=f"#{out_fill_2}", opacity=out_opacity_2))
|
||||||
out_fill_2 + ";opacity:" + str(
|
|
||||||
out_opacity_2) + ';">' + text + "</text>")
|
|
||||||
if out_fill:
|
if out_fill:
|
||||||
self.output_file.write(
|
self.svg.add(Text(
|
||||||
f'<text x="{x}" y="{y}" style="font-size:' +
|
text, (x, y), font_size=size, text_anchor="middle",
|
||||||
str(
|
font_family="Roboto", fill=f"#{out_fill}",
|
||||||
size) + ";text-anchor:middle;font-family:Roboto;fill:#" +
|
stroke_linejoin="round", stroke_width=3,
|
||||||
out_fill + ";stroke-linejoin:round;stroke-width:3;stroke:#" +
|
stroke=f"#{out_fill}", opacity=out_opacity))
|
||||||
out_fill + ";opacity:" + str(
|
self.svg.add(Text(
|
||||||
out_opacity) + ';">' + text + "</text>")
|
text, (x, y), font_size=size, text_anchor="middle",
|
||||||
self.output_file.write(
|
font_family="Roboto", fill=f"#{fill}"))
|
||||||
f'<text x="{x}" y="{y}" style="font-size:' +
|
|
||||||
str(size) + ";text-anchor:middle;font-family:Roboto;fill:#" +
|
|
||||||
fill + ';">' + text + "</text>")
|
|
||||||
|
|
||||||
def wr(self, text, x, y, fill, text_y, size=10):
|
def wr(self, text, x, y, fill, text_y, size=10):
|
||||||
text = text[:26] + ("..." if len(text) > 26 else "")
|
text = text[:26] + ("..." if len(text) > 26 else "")
|
||||||
|
@ -268,16 +258,12 @@ class Painter:
|
||||||
node_2 = self.map_.node_map[way.nodes[i + 1]]
|
node_2 = self.map_.node_map[way.nodes[i + 1]]
|
||||||
flung_1 = self.flinger.fling(Geo(node_1.lat, node_1.lon))
|
flung_1 = self.flinger.fling(Geo(node_1.lat, node_1.lon))
|
||||||
flung_2 = self.flinger.fling(Geo(node_2.lat, node_2.lon))
|
flung_2 = self.flinger.fling(Geo(node_2.lat, node_2.lon))
|
||||||
shifted_1 = np.add(flung_1, shift_1)
|
|
||||||
shifted_2 = np.add(flung_2, shift_2)
|
self.svg.add(self.svg.path(
|
||||||
self.output_file.write(
|
d=("M", np.add(flung_1, shift_1), "L",
|
||||||
f'<path d="M '
|
np.add(flung_2, shift_1), np.add(flung_2, shift_2),
|
||||||
f"{shifted_1[0]},{shifted_1[1]} L "
|
np.add(flung_1, shift_2), "Z"),
|
||||||
f"{shifted_1[0]},{shifted_1[1]} "
|
fill=f"#{color}", stroke=f"#{color}", stroke_width=1))
|
||||||
f"{shifted_2[0]},{shifted_2[1]} "
|
|
||||||
f'{shifted_2[0]},{shifted_2[1]} Z" '
|
|
||||||
f'style="fill:#{color};stroke:#{color};'
|
|
||||||
f'stroke-width:1;" />\n')
|
|
||||||
elif way.path:
|
elif way.path:
|
||||||
# TODO: implement
|
# TODO: implement
|
||||||
pass
|
pass
|
||||||
|
@ -289,34 +275,31 @@ class Painter:
|
||||||
if way.kind == "way":
|
if way.kind == "way":
|
||||||
if way.nodes:
|
if way.nodes:
|
||||||
path = get_path(way.nodes, [0, 0], self.map_, self.flinger)
|
path = get_path(way.nodes, [0, 0], self.map_, self.flinger)
|
||||||
self.output_file.write(
|
self.svg.add(Path(d=path, style=way.style))
|
||||||
f'<path d="{path}" style="' + way.style + '" />\n')
|
|
||||||
else:
|
else:
|
||||||
self.output_file.write(
|
self.svg.add(Path(d=way.path, style=way.style))
|
||||||
f'<path d="{way.path}" style="' + way.style + '" />\n')
|
|
||||||
|
|
||||||
# Building shade
|
# Building shade
|
||||||
|
|
||||||
self.output_file.write('<g style="opacity:0.1;">\n')
|
building_shade = Group(opacity=0.1)
|
||||||
|
|
||||||
for way in ways:
|
for way in ways:
|
||||||
if way.kind == "building":
|
if way.kind != "building" or not way.nodes:
|
||||||
if way.nodes:
|
continue
|
||||||
shift = [-5, 5]
|
shift = [-5, 5]
|
||||||
if way.levels:
|
if way.levels:
|
||||||
shift = [-5 * way.levels, 5 * way.levels]
|
shift = [-5 * way.levels, 5 * way.levels]
|
||||||
for i in range(len(way.nodes) - 1):
|
for i in range(len(way.nodes) - 1):
|
||||||
node_1 = self.map_.node_map[way.nodes[i]]
|
node_1 = self.map_.node_map[way.nodes[i]]
|
||||||
node_2 = self.map_.node_map[way.nodes[i + 1]]
|
node_2 = self.map_.node_map[way.nodes[i + 1]]
|
||||||
flung_1 = self.flinger.fling(Geo(node_1.lat, node_1.lon))
|
flung_1 = self.flinger.fling(Geo(node_1.lat, node_1.lon))
|
||||||
flung_2 = self.flinger.fling(Geo(node_2.lat, node_2.lon))
|
flung_2 = self.flinger.fling(Geo(node_2.lat, node_2.lon))
|
||||||
self.output_file.write(
|
building_shade.add(Path(
|
||||||
f'<path d="M '
|
("M", flung_1, "L", flung_2, np.add(flung_2, shift),
|
||||||
f'{flung_1[0]},{flung_1[1]} L '
|
np.add(flung_1, shift), "Z"),
|
||||||
f"{flung_2[0]},{flung_2[1]} "
|
fill="#000000", stroke="#000000", stroke_width=1))
|
||||||
f"{flung_2[0] + shift[0]},{flung_2[1] + shift[1]} "
|
|
||||||
f'{flung_1[0] + shift[0]},{flung_1[1] + shift[1]} Z" '
|
self.svg.add(building_shade)
|
||||||
f'style="fill:#000000;stroke:#000000;stroke-width:1;" />\n')
|
|
||||||
self.output_file.write("</g>\n")
|
|
||||||
|
|
||||||
# Building walls
|
# Building walls
|
||||||
|
|
||||||
|
@ -327,18 +310,16 @@ class Painter:
|
||||||
# Building roof
|
# Building roof
|
||||||
|
|
||||||
for way in ways:
|
for way in ways:
|
||||||
if way.kind == "building":
|
if way.kind != "building":
|
||||||
if way.nodes:
|
continue
|
||||||
shift = [0, -3]
|
if way.nodes:
|
||||||
if way.levels:
|
shift = [0, -3]
|
||||||
shift = [0 * way.levels, min(-3, -1 * way.levels)]
|
if way.levels:
|
||||||
path = get_path(way.nodes, shift, self.map_, self.flinger)
|
shift = [0 * way.levels, min(-3, -1 * way.levels)]
|
||||||
self.output_file.write(
|
path = get_path(way.nodes, shift, self.map_, self.flinger)
|
||||||
f'<path d="{path}" style="{way.style};opacity:1;" />\n')
|
self.svg.add(Path(d=path, style=way.style, opacity=1))
|
||||||
else:
|
else:
|
||||||
self.output_file.write(
|
self.svg.add(Path(d=way.path, style=way.style, opacity=1))
|
||||||
f'<path d="{way.path}" '
|
|
||||||
f'style="{way.style};opacity:1;" />\n')
|
|
||||||
|
|
||||||
# Trees
|
# Trees
|
||||||
|
|
||||||
|
@ -347,11 +328,10 @@ class Painter:
|
||||||
node.tags["natural"] == "tree" and
|
node.tags["natural"] == "tree" and
|
||||||
"diameter_crown" in node.tags):
|
"diameter_crown" in node.tags):
|
||||||
continue
|
continue
|
||||||
self.output_file.circle(
|
self.svg.add(Circle(
|
||||||
float(node.x) + (random.random() - 0.5) * 10,
|
(float(node.x), float(node.y)),
|
||||||
float(node.y) + (random.random() - 0.5) * 10,
|
|
||||||
float(node.tags["diameter_crown"]) * 1.2,
|
float(node.tags["diameter_crown"]) * 1.2,
|
||||||
color='688C44', fill='688C44', opacity=0.3)
|
fill="#688C44", stroke="#688C44", opacity=0.3))
|
||||||
|
|
||||||
# All other nodes
|
# All other nodes
|
||||||
|
|
||||||
|
@ -386,20 +366,15 @@ class Painter:
|
||||||
def draw_point(self, shape, x, y, fill, size=16, xx=0, yy=0, tags=None):
|
def draw_point(self, shape, x, y, fill, size=16, xx=0, yy=0, tags=None):
|
||||||
x = int(float(x))
|
x = int(float(x))
|
||||||
y = int(float(y))
|
y = int(float(y))
|
||||||
self.output_file.write(
|
path = self.svg.path(
|
||||||
'<path d="' + shape + '" style="fill:#' + fill +
|
d=shape, fill=f"#{fill}", fill_opacity=1,
|
||||||
';fill-opacity:1" transform="translate(' +
|
transform=f"translate({x - size / 2.0 - xx * 16},"
|
||||||
str(x - size / 2.0 - xx * 16) + "," + str(
|
f"{y - size / 2.0 - yy * 16})")
|
||||||
y - size / 2.0 - yy * 16) +
|
path.set_desc(title="\n".join(map(lambda x: x + ": " + tags[x], tags)))
|
||||||
')">')
|
self.svg.add(path)
|
||||||
if tags:
|
|
||||||
self.output_file.write("<title>")
|
|
||||||
self.output_file.write(
|
|
||||||
"\n".join(map(lambda x: x + ": " + tags[x], tags)))
|
|
||||||
self.output_file.write("</title>")
|
|
||||||
self.output_file.write("</path>\n")
|
|
||||||
|
|
||||||
def draw_point_outline(self, shape, x, y, fill, mode="default", size=16, xx=0, yy=0):
|
def draw_point_outline(
|
||||||
|
self, shape, x, y, fill, mode="default", size=16, xx=0, yy=0):
|
||||||
x = int(float(x))
|
x = int(float(x))
|
||||||
y = int(float(y))
|
y = int(float(y))
|
||||||
opacity = 0.5
|
opacity = 0.5
|
||||||
|
@ -413,12 +388,12 @@ class Painter:
|
||||||
if Y > 200:
|
if Y > 200:
|
||||||
outline_fill = "000000"
|
outline_fill = "000000"
|
||||||
opacity = 0.7
|
opacity = 0.7
|
||||||
self.output_file.write(
|
self.svg.add(self.svg.path(
|
||||||
'<path d="' + shape + '" style="fill:#' + outline_fill + ";opacity:" +
|
d=shape, fill=f"#{outline_fill}", opacity=opacity,
|
||||||
str(opacity) + ";" + "stroke:#" + outline_fill +
|
stroke=f"#{outline_fill}", stroke_width=stroke_width,
|
||||||
f';stroke-width:{stroke_width};stroke-linejoin:round;" ' + 'transform="translate(' +
|
stroke_linejoin="round",
|
||||||
str(x - size / 2.0 - xx * 16) + "," + str(y - size / 2.0 - yy * 16) +
|
transform=f"translate({x - size / 2.0 - xx * 16},"
|
||||||
')" />\n')
|
f"{y - size / 2.0 - yy * 16})"))
|
||||||
|
|
||||||
|
|
||||||
def check_level_number(tags, level):
|
def check_level_number(tags, level):
|
||||||
|
@ -453,9 +428,9 @@ def main():
|
||||||
if not options:
|
if not options:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
background_color = "EEEEEE"
|
background_color = "#EEEEEE"
|
||||||
if options.mode in ["user-coloring", "time"]:
|
if options.mode in ["user-coloring", "time"]:
|
||||||
background_color = "111111"
|
background_color = "#111111"
|
||||||
|
|
||||||
if options.input_file_name:
|
if options.input_file_name:
|
||||||
input_file_name = options.input_file_name
|
input_file_name = options.input_file_name
|
||||||
|
@ -480,18 +455,18 @@ def main():
|
||||||
print("Fatal: no such file: " + file_name + ".")
|
print("Fatal: no such file: " + file_name + ".")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
map_ = osm_reader.parse_osm_file(
|
osm_reader.parse_osm_file(
|
||||||
file_name, parse_ways=options.draw_ways,
|
file_name, parse_ways=options.draw_ways,
|
||||||
parse_relations=options.draw_ways, full=full)
|
parse_relations=options.draw_ways, full=full)
|
||||||
|
|
||||||
output_file = svg.SVG(open(options.output_file_name, "w+"))
|
map_: Map = osm_reader.map_
|
||||||
|
|
||||||
w, h = list(map(lambda x: float(x), options.size.split(",")))
|
w, h = list(map(lambda x: float(x), options.size.split(",")))
|
||||||
|
|
||||||
output_file.begin(w, h)
|
svg: svgwrite.Drawing = \
|
||||||
output_file.write(
|
svgwrite.Drawing(options.output_file_name, size=(w, h))
|
||||||
"<title>Rӧntgen</title><style>path:hover {stroke: #FF0000;}</style>\n")
|
|
||||||
output_file.rect(0, 0, w, h, color=background_color)
|
svg.add(Rect((0, 0), (w, h), fill=background_color))
|
||||||
|
|
||||||
min1 = Geo(boundary_box[1], boundary_box[0])
|
min1 = Geo(boundary_box[1], boundary_box[0])
|
||||||
max1 = Geo(boundary_box[3], boundary_box[2])
|
max1 = Geo(boundary_box[3], boundary_box[2])
|
||||||
|
@ -537,18 +512,18 @@ def main():
|
||||||
show_missing_tags=options.show_missing_tags, overlap=options.overlap,
|
show_missing_tags=options.show_missing_tags, overlap=options.overlap,
|
||||||
draw_nodes=options.draw_nodes, mode=options.mode,
|
draw_nodes=options.draw_nodes, mode=options.mode,
|
||||||
draw_captions=options.draw_captions,
|
draw_captions=options.draw_captions,
|
||||||
map_=map_, flinger=flinger, output_file=output_file, icons=icons,
|
map_=map_, flinger=flinger, svg=svg, icons=icons,
|
||||||
scheme=scheme)
|
scheme=scheme)
|
||||||
painter.draw(constructor.nodes, constructor.ways, points)
|
painter.draw(constructor.nodes, constructor.ways, points)
|
||||||
|
|
||||||
if flinger.space[0] == 0:
|
if flinger.space[0] == 0:
|
||||||
output_file.rect(0, 0, w, flinger.space[1], color="FFFFFF")
|
svg.add(Rect((0, 0), (w, flinger.space[1]), fill="#FFFFFF"))
|
||||||
output_file.rect(
|
svg.add(Rect(
|
||||||
0, h - flinger.space[1], w, flinger.space[1], color="FFFFFF")
|
(0, h - flinger.space[1]), (w, flinger.space[1]), fill="#FFFFFF"))
|
||||||
if flinger.space[1] == 0:
|
if flinger.space[1] == 0:
|
||||||
output_file.rect(0, 0, flinger.space[0], h, color="FFFFFF")
|
svg.add(Rect((0, 0), (flinger.space[0], h), fill="#FFFFFF"))
|
||||||
output_file.rect(
|
svg.add(Rect(
|
||||||
w - flinger.space[0], 0, flinger.space[0], h, color="FFFFFF")
|
(w - flinger.space[0], 0), (flinger.space[0], h), fill="#FFFFFF"))
|
||||||
|
|
||||||
if options.show_index:
|
if options.show_index:
|
||||||
print(min1.lon, max1.lon)
|
print(min1.lon, max1.lon)
|
||||||
|
@ -595,12 +570,13 @@ def main():
|
||||||
t2 = flinger.fling(Geo(
|
t2 = flinger.fling(Geo(
|
||||||
min1.lat + (i + 1) * lat_step,
|
min1.lat + (i + 1) * lat_step,
|
||||||
min1.lon + (j + 1) * lon_step))
|
min1.lon + (j + 1) * lon_step))
|
||||||
output_file.text(
|
svg.add(Text(
|
||||||
((t1 + t2) * 0.5)[0], ((t1 + t2) * 0.5)[1] + 40,
|
str(int(matrix[i][j])),
|
||||||
str(int(matrix[i][j])), size=80, color="440000",
|
(((t1 + t2) * 0.5)[0], ((t1 + t2) * 0.5)[1] + 40),
|
||||||
opacity=0.1, align="center")
|
font_size=80, fill="440000",
|
||||||
|
opacity=0.1, align="center"))
|
||||||
|
|
||||||
output_file.end()
|
svg.write(open(options.output_file_name, "w"))
|
||||||
|
|
||||||
top_missing_tags = \
|
top_missing_tags = \
|
||||||
sorted(missing_tags.keys(), key=lambda x: -missing_tags[x])
|
sorted(missing_tags.keys(), key=lambda x: -missing_tags[x])
|
||||||
|
|
|
@ -3,7 +3,7 @@ Reading OpenStreetMap data from XML file.
|
||||||
|
|
||||||
Author: Sergey Vartanov
|
Author: Sergey Vartanov
|
||||||
"""
|
"""
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Dict, List, Optional
|
||||||
|
|
||||||
from roentgen import ui
|
from roentgen import ui
|
||||||
|
|
||||||
|
|
245
roentgen/svg.py
245
roentgen/svg.py
|
@ -1,245 +0,0 @@
|
||||||
"""
|
|
||||||
Very simple SVG library.
|
|
||||||
|
|
||||||
Author: Sergey Vartanov (me@enzet.ru)
|
|
||||||
"""
|
|
||||||
|
|
||||||
import math
|
|
||||||
|
|
||||||
|
|
||||||
class SVG:
|
|
||||||
def __init__(self, file_):
|
|
||||||
self.file = file_
|
|
||||||
self.index = 0
|
|
||||||
|
|
||||||
def begin(self, width, height):
|
|
||||||
self.file.write(
|
|
||||||
'<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n\n')
|
|
||||||
self.file.write(
|
|
||||||
f'''<svg version="1.1" baseProfile="full"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
||||||
xmlns:ev="http://www.w3.org/2001/xml-events"
|
|
||||||
bgcolor="#e5e9e6" width="{width}" height="{height}">\n''')
|
|
||||||
|
|
||||||
def end(self):
|
|
||||||
self.file.write('</svg>\n')
|
|
||||||
|
|
||||||
def path(
|
|
||||||
self, path: str, color: str = "black", width: float = 1,
|
|
||||||
fill: str = "none", end: str = "butt", id: str = None,
|
|
||||||
color2: str = None, gx1: float = 0, gy1: float = 0, gx2: float = 0,
|
|
||||||
gy2: float = 0, dash: str = None, dashoffset: str = None,
|
|
||||||
opacity: float = 1, transform: str = None):
|
|
||||||
|
|
||||||
if color2:
|
|
||||||
self.index += 1
|
|
||||||
self.file.write(
|
|
||||||
f'<defs><linearGradient id="grad{str(self.index)}" '
|
|
||||||
f'x1="{str(gx1)}" y1="{str(gy1)}" '
|
|
||||||
f'x2="{str(gx2)}" y2="{str(gy2)}" '
|
|
||||||
f'gradientUnits="userSpaceOnUse">'
|
|
||||||
f'<stop style="stop-color:{self.get_color(color)};'
|
|
||||||
f'stop-opacity:1;" offset="0" />'
|
|
||||||
f'<stop style="stop-color:{self.get_color(color2)};'
|
|
||||||
f'stop-opacity:1;" offset="1" /></linearGradient></defs>\n')
|
|
||||||
|
|
||||||
self.file.write(' <path d = "' + path + '" ')
|
|
||||||
|
|
||||||
if id:
|
|
||||||
self.file.write(f'id="{id}" ')
|
|
||||||
if transform:
|
|
||||||
self.file.write('transform="' + transform + '" ')
|
|
||||||
|
|
||||||
self.file.write('style = "')
|
|
||||||
if not color2:
|
|
||||||
self.file.write('stroke:' + self.get_color(color) + '; ')
|
|
||||||
else:
|
|
||||||
self.file.write('stroke:url(#grad' + str(self.index) + '); ')
|
|
||||||
self.file.write('stroke-width:' + str(width) + '; ')
|
|
||||||
self.file.write('fill:' + self.get_color(fill) + '; ')
|
|
||||||
self.file.write('stroke-linecap:' + end + '; ')
|
|
||||||
if opacity != 1:
|
|
||||||
self.file.write('opacity:' + str(opacity) + '; ')
|
|
||||||
if dash:
|
|
||||||
self.file.write('stroke-dasharray:' + dash + '; ')
|
|
||||||
if dashoffset:
|
|
||||||
self.file.write('stroke-dashoffset:' + str(dashoffset) + '; ')
|
|
||||||
self.file.write('" />\n')
|
|
||||||
|
|
||||||
def line(
|
|
||||||
self, x1, y1, x2, y2, width=1, color='black', end='butt', id_=None,
|
|
||||||
color2=None, gx1=None, gy1=None, gx2=None, gy2=None, dash=None,
|
|
||||||
dashoffset=None, opacity=None):
|
|
||||||
|
|
||||||
if color2:
|
|
||||||
if not gx1:
|
|
||||||
gx1 = x1
|
|
||||||
if not gy1:
|
|
||||||
gy1 = y1
|
|
||||||
if not gx2:
|
|
||||||
gx2 = x2
|
|
||||||
if not gy2:
|
|
||||||
gy2 = y2
|
|
||||||
self.index += 1
|
|
||||||
self.file.write(
|
|
||||||
f'<defs><linearGradient id="grad{str(self.index)}" '
|
|
||||||
f'x1="{str(gx1)}" y1="{str(gy1)}" x2="{str(gx2)}" '
|
|
||||||
f'y2="{str(gy2)}" gradientUnits="userSpaceOnUse">\n'
|
|
||||||
f'<stop style="stop-color:#{str(color)};stop-opacity:1;" '
|
|
||||||
f'offset="0" />'
|
|
||||||
f'<stop style=\"stop-color:#{str(color2)};stop-opacity:1;" '
|
|
||||||
f'offset="1" />'
|
|
||||||
f'</linearGradient></defs>\n')
|
|
||||||
self.file.write(
|
|
||||||
f' <path d = "M {str(x1)},{str(y1)} {str(x2)},{str(y2)}" ')
|
|
||||||
if id_:
|
|
||||||
self.file.write(f'id="{id_}" ')
|
|
||||||
self.file.write('style="')
|
|
||||||
if not color2:
|
|
||||||
self.file.write(f'stroke:{self.get_color(color)}; ')
|
|
||||||
else:
|
|
||||||
self.file.write(f'stroke:url(#grad{self.index}); ')
|
|
||||||
self.file.write(f'stroke-width:{width}; ')
|
|
||||||
self.file.write(f'stroke-linecap:{end}; ')
|
|
||||||
if dash:
|
|
||||||
self.file.write('stroke-dasharray:' + dash + '; ')
|
|
||||||
if dashoffset:
|
|
||||||
self.file.write('stroke-dashoffset:' + str(dashoffset) + '; ')
|
|
||||||
if opacity:
|
|
||||||
self.file.write('opacity: ' + str(opacity) + '; ')
|
|
||||||
self.file.write('" />\n')
|
|
||||||
|
|
||||||
def rect(
|
|
||||||
self, x, y, width, height, color='black', id=None, rx=0, ry=0,
|
|
||||||
opacity=1.0, stroke_color='none', stroke_width=1.0):
|
|
||||||
|
|
||||||
self.file.write(' <rect x = "' + str(x) + '" y = "' + str(y) + '" rx = "' + str(rx) + '" ry = "' + str(ry) +
|
|
||||||
'" ')
|
|
||||||
self.file.write(' width = "' + str(width) + '" height = "' + str(height) + '" ')
|
|
||||||
if id:
|
|
||||||
self.file.write('id = "' + id + '" ')
|
|
||||||
self.file.write('style = "')
|
|
||||||
if opacity != 1:
|
|
||||||
self.file.write('opacity:' + str(opacity) + '; ')
|
|
||||||
self.file.write('fill:' + self.get_color(color) + '; ')
|
|
||||||
self.file.write('stroke:' + self.get_color(stroke_color) + '; ')
|
|
||||||
self.file.write('stroke-width:' + str(stroke_width) + '; ')
|
|
||||||
self.file.write('" />\n')
|
|
||||||
|
|
||||||
def curve(self, x1, y1, x2, y2, x3, y3, x4, y4, id=None, width=1, color='black'):
|
|
||||||
self.file.write(' <path d = "M ' + str(x1) + ',' + str(y1) + ' C ' + str(x2) + ',' + str(y2) + ' ')
|
|
||||||
self.file.write(str(x3) + ',' + str(y3) + ' ' + str(x4) + ',' + str(y4) + '" ')
|
|
||||||
self.file.write('style = "')
|
|
||||||
self.file.write('stroke:' + self.get_color(color) + '; ')
|
|
||||||
self.file.write('stroke-width:' + str(width) + '; ')
|
|
||||||
self.file.write('fill: none; ')
|
|
||||||
self.file.write('" />\n')
|
|
||||||
|
|
||||||
def rhombus(self, x, y, width, height, color='black', id=None):
|
|
||||||
self.file.write(''' <path d = "M %5.1f %5.1f L %5.1f %5.1f L %5.1f %5.1f
|
|
||||||
L %5.1f %5.1f L %5.1f %5.1f" ''' % (x, y - height, x + width, y, x, y + height, x - width, y, x,
|
|
||||||
y - height))
|
|
||||||
if id:
|
|
||||||
self.file.write('id = "' + id + '" ')
|
|
||||||
self.file.write('style = "')
|
|
||||||
self.file.write('fill:' + self.get_color(color) + '; ')
|
|
||||||
self.file.write('" />\n')
|
|
||||||
|
|
||||||
def circle(self, x, y, d, color='black', color2='white', fill='none',
|
|
||||||
opacity=None, width=1, id_=None, gx=0, gy=0, gr=0, fx=0, fy=0):
|
|
||||||
is_grad = gx != 0 or gy != 0 or gr != 0
|
|
||||||
if is_grad:
|
|
||||||
self.index += 1
|
|
||||||
self.file.write(
|
|
||||||
f'<defs><radialGradient id="grad{str(self.index)}" '
|
|
||||||
f'cx="{gx}%" cy="{gy}%" r="{gr}%" fx="{fx}%" fy="{fy}%">'
|
|
||||||
f'<stop offset="0%" style="stop-color:rgb(255,255,255);'
|
|
||||||
f'stop-opacity:0" /><stop offset="100%" '
|
|
||||||
f'style="stop-color:{self.get_color(color)};stop-opacity:1" />'
|
|
||||||
f'</radialGradient></defs>\n')
|
|
||||||
c = 0.577
|
|
||||||
self.file.write(
|
|
||||||
f' <path d = "'
|
|
||||||
f'M {x:5.1f} {y + d:5.1f} '
|
|
||||||
f'C {x - d * c:5.1f} {y + d:5.1f} {x - d:5.1f} {y + d * c:5.1f} '
|
|
||||||
f'{x - d:5.1f} {y:5.1f} '
|
|
||||||
f'C {x - d:5.1f} {y - d * c:5.1f} {x - d * c:5.1f} {y - d:5.1f} '
|
|
||||||
f'{x:5.1f} {y - d:5.1f} '
|
|
||||||
f'C {x + d * c:5.1f} {y - d:5.1f} {x + d:5.1f} {y - d * c:5.1f} '
|
|
||||||
f'{x + d:5.1f} {y:5.1f} '
|
|
||||||
f'C {x + d:5.1f} {y + d * c:5.1f} {x + d * c:5.1f} {y + d:5.1f} '
|
|
||||||
f'{x:5.1f} {y + d:5.1f}" ')
|
|
||||||
|
|
||||||
if id_:
|
|
||||||
self.file.write('id = "' + id_ + '" ')
|
|
||||||
self.file.write('style = "')
|
|
||||||
if is_grad:
|
|
||||||
self.file.write('fill:url(#grad' + str(self.index) + '); ')
|
|
||||||
else:
|
|
||||||
self.file.write('fill:' + self.get_color(fill) + '; ')
|
|
||||||
self.file.write('stroke-width:' + str(width) + '; ')
|
|
||||||
self.file.write('stroke:' + self.get_color(color) + '; ')
|
|
||||||
if opacity:
|
|
||||||
self.file.write('opacity:' + str(opacity) + '; ')
|
|
||||||
self.file.write('" />\n')
|
|
||||||
|
|
||||||
def text(self, x, y, text, font='Myriad Pro', size='10', align='left',
|
|
||||||
color='black', id=None, weight=None, letter_spacing=None, angle=None,
|
|
||||||
opacity=None):
|
|
||||||
"""
|
|
||||||
Drawing SVG <text> element.
|
|
||||||
"""
|
|
||||||
if angle is None:
|
|
||||||
self.file.write(f' <text x="{str(x)}" y="{str(y)}" ')
|
|
||||||
else:
|
|
||||||
self.file.write(' <text x="0" y="0" ')
|
|
||||||
self.file.write('font-size = "' + str(size) + '" ')
|
|
||||||
if id:
|
|
||||||
self.file.write('id = "' + id + '" ')
|
|
||||||
if not (angle is None) and angle <= 0:
|
|
||||||
align = 'end'
|
|
||||||
if align == 'right':
|
|
||||||
align = 'end'
|
|
||||||
if align == 'center':
|
|
||||||
align = 'middle'
|
|
||||||
self.file.write('style = "')
|
|
||||||
self.file.write('text-anchor:' + align + '; ')
|
|
||||||
if opacity:
|
|
||||||
self.file.write('opacity:' + str(opacity) + '; ')
|
|
||||||
self.file.write('font-family: ' + font + '; ')
|
|
||||||
self.file.write('fill: ' + self.get_color(color) + '; ')
|
|
||||||
if weight == 'bold':
|
|
||||||
self.file.write('font-weight:bold; ')
|
|
||||||
if letter_spacing:
|
|
||||||
self.file.write('letter-spacing:' + str(letter_spacing) + '; ')
|
|
||||||
self.file.write('"')
|
|
||||||
if not (angle is None):
|
|
||||||
if math.sin(angle) > 0:
|
|
||||||
trans = 'transform = "matrix(' + str(math.sin(angle)) + ',' + str(math.cos(angle)) + ',' + \
|
|
||||||
str(-math.cos(angle)) + ',' + str(math.sin(angle)) + ',' + str(x) + ',' + str(y) + ')"'
|
|
||||||
else:
|
|
||||||
trans = 'transform = "matrix(' + str(math.sin(angle + math.pi)) + ',' + str(math.cos(angle + math.pi)) + ',' + \
|
|
||||||
str(-math.cos(angle + math.pi)) + ',' + str(math.sin(angle + math.pi)) + ',' + str(x) + ',' + str(y) + ')"'
|
|
||||||
self.file.write(' ' + trans)
|
|
||||||
self.file.write('>')
|
|
||||||
self.file.write(text)
|
|
||||||
self.file.write('</text>\n')
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_color(color):
|
|
||||||
if color == 'none':
|
|
||||||
return 'none'
|
|
||||||
if color == 'black':
|
|
||||||
return 'black'
|
|
||||||
return '#' + str(color)
|
|
||||||
|
|
||||||
def begin_layer(self, name):
|
|
||||||
self.file.write(f'<g id="{name}" label="{name}" ')
|
|
||||||
self.file.write('inkscape:groupmode="layer">\n')
|
|
||||||
|
|
||||||
def end_layer(self):
|
|
||||||
self.file.write('</g>\n')
|
|
||||||
|
|
||||||
def write(self, raw_code):
|
|
||||||
self.file.write(raw_code)
|
|
|
@ -8,7 +8,9 @@ from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
def parse_options(args):
|
def parse_options(args):
|
||||||
|
"""
|
||||||
|
Parse Röntgen command-line options.
|
||||||
|
"""
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
@ -56,9 +58,9 @@ def parse_options(args):
|
||||||
"--show-index", dest="show_index", action="store_true")
|
"--show-index", dest="show_index", action="store_true")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--no-show-index", dest="show_index", action="store_false")
|
"--no-show-index", dest="show_index", action="store_false")
|
||||||
parser.add_argument("--mode", dest="mode", default="normal")
|
parser.add_argument("--mode", default="normal")
|
||||||
parser.add_argument("--seed", dest="seed", default="")
|
parser.add_argument("--seed", default="")
|
||||||
parser.add_argument("--level", dest="level", default=None)
|
parser.add_argument("--level", default=None)
|
||||||
|
|
||||||
arguments = parser.parse_args(args[1:])
|
arguments = parser.parse_args(args[1:])
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue