Generate standalone SVG

This commit is contained in:
s3lph 2022-10-14 20:32:58 +02:00
parent 286534188a
commit d8fe6a9d46
Signed by: s3lph
GPG key ID: 8AC98A811E5BEFF5
2 changed files with 49 additions and 29 deletions

View file

@ -46,7 +46,10 @@ class OutputPaths:
raise AttributeError(f'Path exists but is not a directory: {path}') raise AttributeError(f'Path exists but is not a directory: {path}')
os.makedirs(path, exist_ok=True) os.makedirs(path, exist_ok=True)
self.path = path self.path = path
self.svg_path = os.path.join(path, 'map.svg') self.svg_web_path = os.path.join(path, 'web.svg')
self.svg_standalone_path = os.path.join(path, 'standalone.svg')
self.font_path = os.path.join(path, 'style/font.ttf')
self.css_path = os.path.join(path, 'style/erfamap.css')
self.png_path = os.path.join(path, 'map.png') self.png_path = os.path.join(path, 'map.png')
self.html_path = os.path.join(path, 'erfamap.html') self.html_path = os.path.join(path, 'erfamap.html')
self.imagemap_path = os.path.join(path, 'imagemap.html') self.imagemap_path = os.path.join(path, 'imagemap.html')
@ -258,6 +261,8 @@ class Erfa(Drawable):
self.lat = lat self.lat = lat
self.x, self.y = ns.projection(lon, lat) self.x, self.y = ns.projection(lon, lat)
self.display_name = display_name if display_name is not None else name self.display_name = display_name if display_name is not None else name
if city.lower() not in self.display_name.lower():
self.display_name += f' ({city})'
self.web = web self.web = web
self.radius = radius self.radius = radius
@ -322,7 +327,7 @@ class Erfa(Drawable):
return None return None
@classmethod @classmethod
def from_api(cls, name, attr, radius, ns): def from_api(cls, ns, name, attr, radius):
city = attr['Chaostreff-City'][0] city = attr['Chaostreff-City'][0]
location = cls.address_lookup(attr) location = cls.address_lookup(attr)
if location is None: if location is None:
@ -393,6 +398,7 @@ class Erfa(Drawable):
erfa = cls.from_api(ns, name, attr['printouts'], radius) erfa = cls.from_api(ns, name, attr['printouts'], radius)
erfas.append(erfa) erfas.append(erfa)
except BaseException as e: except BaseException as e:
breakpoint()
print(e) print(e)
continue continue
@ -615,10 +621,9 @@ def compute_bbox(ns):
] ]
def optimize_text_layout(ns, erfas, chaostreffs, width, svg): def optimize_text_layout(ns, font, erfas, chaostreffs, width, svg):
# Load the font and measure its various different heights # Measure the various different font heights
font = ImageFont.truetype(ns.font, ns.font_size)
pil = ImageDraw(Image.new('P', (1, 1))) pil = ImageDraw(Image.new('P', (1, 1)))
xheight = -pil.textbbox((0,0), 'x', font=font, anchor='ls')[1] xheight = -pil.textbbox((0,0), 'x', font=font, anchor='ls')[1]
capheight = -pil.textbbox((0,0), 'A', font=font, anchor='ls')[1] capheight = -pil.textbbox((0,0), 'A', font=font, anchor='ls')[1]
@ -763,6 +768,9 @@ def create_imagemap(ns, size, parent, erfas, chaostreffs, texts):
def create_svg(ns, bbox): def create_svg(ns, bbox):
print('Creating SVG image') print('Creating SVG image')
# Load the font used for the labels
font = ImageFont.truetype(ns.font, ns.font_size)
# Convert from WGS84 lon, lat to chosen projection # Convert from WGS84 lon, lat to chosen projection
svg_box = ns.projection.setup(ns.scale_x, ns.scale_y, bbox=bbox) svg_box = ns.projection.setup(ns.scale_x, ns.scale_y, bbox=bbox)
rectbox = [0, 0, svg_box[0], svg_box[1]] rectbox = [0, 0, svg_box[0], svg_box[1]]
@ -786,22 +794,27 @@ def create_svg(ns, bbox):
rectbox[3] = max(y, rectbox[3]) rectbox[3] = max(y, rectbox[3])
print('Copying stylesheet and font') print('Copying stylesheet and font')
dst = os.path.join(ns.output_directory.path, ns.stylesheet) os.makedirs(os.path.dirname(ns.output_directory.font_path), exist_ok=True)
os.makedirs(os.path.dirname(dst), exist_ok=True) shutil.copyfile(ns.font, ns.output_directory.font_path)
shutil.copyfile(ns.stylesheet, dst) os.makedirs(os.path.dirname(ns.output_directory.css_path), exist_ok=True)
dst = os.path.join(ns.output_directory.path, ns.font) fontrelpath = os.path.relpath(ns.output_directory.font_path,
os.makedirs(os.path.dirname(dst), exist_ok=True) start=os.path.dirname(ns.output_directory.css_path))
shutil.copyfile(ns.font, dst) with open(ns.stylesheet, 'r') as src:
with open(ns.output_directory.css_path, 'w') as dst:
dst.write(f'''
@font-face {{
font-family: "{font.font.family}";
src: url("{fontrelpath}");
}}
''')
dst.write(src.read())
svg = etree.Element('svg', svg = etree.Element('svg',
xmlns='http://www.w3.org/2000/svg', xmlns='http://www.w3.org/2000/svg',
viewBox=f'0 0 {svg_box[0]} {svg_box[1]}', viewBox=f'0 0 {svg_box[0]} {svg_box[1]}',
width=str(svg_box[0]), height=str(svg_box[1])) width=str(svg_box[0]), height=str(svg_box[1]))
style = etree.Element('style', type='text/css')
style.text = f'@import url({ns.stylesheet})'
svg.append(style)
bg = etree.Element('rect', bg = etree.Element('rect',
id='background', id='background',
x=str(rectbox[0]), x=str(rectbox[0]),
@ -813,7 +826,7 @@ def create_svg(ns, bbox):
# This can take some time, especially if lots of candidates are generated # This can take some time, especially if lots of candidates are generated
print('Layouting labels') print('Layouting labels')
texts = optimize_text_layout(ns, erfas, chaostreffs, width=svg_box[0], svg=svg) texts = optimize_text_layout(ns, font, erfas, chaostreffs, width=svg_box[0], svg=svg)
# Render shortest shapes last s.t. Berlin, Hamburg and Bremen are rendered on top of their surrounding states # Render shortest shapes last s.t. Berlin, Hamburg and Bremen are rendered on top of their surrounding states
states = sorted(states, key=lambda x: -len(x)) states = sorted(states, key=lambda x: -len(x))
@ -827,24 +840,36 @@ def create_svg(ns, bbox):
# Generate SVG, PNG and HTML output files # Generate SVG, PNG and HTML output files
print('Writing SVG') print('Writing Web SVG')
with open(ns.output_directory.svg_path, 'wb') as mapfile: with open(ns.output_directory.svg_web_path, 'wb') as mapfile:
root = etree.ElementTree(svg) root = etree.ElementTree(svg)
root.write(mapfile) root.write(mapfile)
style = etree.Element('style', type='text/css')
with open(ns.stylesheet, 'r') as css:
style.text = css.read()
svg.insert(0, style)
print('Writing Standalone SVG')
with open(ns.output_directory.svg_standalone_path, 'wb') as mapfile:
root = etree.ElementTree(svg)
root.write(mapfile)
print('Writing PNG') print('Writing PNG')
cairosvg.svg2png(url=ns.output_directory.svg_path, write_to=ns.output_directory.png_path, cairosvg.svg2png(url=ns.output_directory.svg_web_path, write_to=ns.output_directory.png_path,
scale=ns.png_scale) scale=ns.png_scale)
print('Writing HTML SVG page') print('Writing HTML SVG page')
html = etree.Element('html') html = etree.Element('html')
head = etree.Element('head') head = etree.Element('head')
link = etree.Element('link', rel='stylesheet', href=ns.stylesheet) link = etree.Element('link', rel='stylesheet', href=ns.output_directory.rel(ns.output_directory.css_path))
head.append(link) head.append(link)
html.append(head) html.append(head)
body = etree.Element('body') body = etree.Element('body')
obj = etree.Element('object', obj = etree.Element('object',
data=ns.output_directory.rel(ns.output_directory.svg_path), data=ns.output_directory.rel(ns.output_directory.svg_web_path),
width=str(svg_box[0]), height=str(svg_box[1])) width=str(svg_box[0]), height=str(svg_box[1]))
create_imagemap(ns, svg_box, obj, erfas, chaostreffs, texts) create_imagemap(ns, svg_box, obj, erfas, chaostreffs, texts)
body.append(obj) body.append(obj)
@ -856,7 +881,7 @@ def create_svg(ns, bbox):
print('Writing HTML Image Map') print('Writing HTML Image Map')
html = etree.Element('html') html = etree.Element('html')
head = etree.Element('head') head = etree.Element('head')
link = etree.Element('link', rel='stylesheet', href=ns.stylesheet) link = etree.Element('link', rel='stylesheet', href=ns.output_directory.rel(ns.output_directory.css_path))
head.append(link) head.append(link)
html.append(head) html.append(head)
body = etree.Element('body') body = etree.Element('body')
@ -916,7 +941,7 @@ def main():
if os.path.exists(ns.cache_directory.chaostreff_info): if os.path.exists(ns.cache_directory.chaostreff_info):
os.unlink(ns.cache_directory.chaostreff_info) os.unlink(ns.cache_directory.chaostreff_info)
print('Retrieving Chaostreffs information') print('Retrieving Chaostreffs information')
Chaostreff.fetch(target=ns.cache_directory.chaostreff_info, radius=ns.dotsize_treff) Chaostreff.fetch(ns, target=ns.cache_directory.chaostreff_info, radius=ns.dotsize_treff)
bbox = compute_bbox(ns) bbox = compute_bbox(ns)

View file

@ -51,13 +51,8 @@ a:hover > text {
fill: #5b8ca7; fill: #5b8ca7;
} }
@font-face {
font-family: 'concertone';
src: url('./concertone-regular.ttf');
}
text.erfalabel { text.erfalabel {
font-family: concertone; font-family: "Concert One";
font-size: 45px; font-size: 45px;
z-index: 100; z-index: 100;
user-select: none; user-select: none;