From d8fe6a9d46b04e737a08554313552facefa28086 Mon Sep 17 00:00:00 2001 From: s3lph Date: Fri, 14 Oct 2022 20:32:58 +0200 Subject: [PATCH] Generate standalone SVG --- generate_map.py | 71 ++++++++++++++++++++++++++++++++--------------- style/erfamap.css | 7 +---- 2 files changed, 49 insertions(+), 29 deletions(-) diff --git a/generate_map.py b/generate_map.py index 1d6018f..a6617d1 100755 --- a/generate_map.py +++ b/generate_map.py @@ -46,7 +46,10 @@ class OutputPaths: raise AttributeError(f'Path exists but is not a directory: {path}') os.makedirs(path, exist_ok=True) 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.html_path = os.path.join(path, 'erfamap.html') self.imagemap_path = os.path.join(path, 'imagemap.html') @@ -258,6 +261,8 @@ class Erfa(Drawable): self.lat = lat self.x, self.y = ns.projection(lon, lat) 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.radius = radius @@ -322,7 +327,7 @@ class Erfa(Drawable): return None @classmethod - def from_api(cls, name, attr, radius, ns): + def from_api(cls, ns, name, attr, radius): city = attr['Chaostreff-City'][0] location = cls.address_lookup(attr) if location is None: @@ -393,6 +398,7 @@ class Erfa(Drawable): erfa = cls.from_api(ns, name, attr['printouts'], radius) erfas.append(erfa) except BaseException as e: + breakpoint() print(e) 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 - font = ImageFont.truetype(ns.font, ns.font_size) + # Measure the various different font heights pil = ImageDraw(Image.new('P', (1, 1))) xheight = -pil.textbbox((0,0), 'x', 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): 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 svg_box = ns.projection.setup(ns.scale_x, ns.scale_y, bbox=bbox) rectbox = [0, 0, svg_box[0], svg_box[1]] @@ -786,22 +794,27 @@ def create_svg(ns, bbox): rectbox[3] = max(y, rectbox[3]) print('Copying stylesheet and font') - dst = os.path.join(ns.output_directory.path, ns.stylesheet) - os.makedirs(os.path.dirname(dst), exist_ok=True) - shutil.copyfile(ns.stylesheet, dst) - dst = os.path.join(ns.output_directory.path, ns.font) - os.makedirs(os.path.dirname(dst), exist_ok=True) - shutil.copyfile(ns.font, dst) + os.makedirs(os.path.dirname(ns.output_directory.font_path), exist_ok=True) + shutil.copyfile(ns.font, ns.output_directory.font_path) + os.makedirs(os.path.dirname(ns.output_directory.css_path), exist_ok=True) + fontrelpath = os.path.relpath(ns.output_directory.font_path, + start=os.path.dirname(ns.output_directory.css_path)) + 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', xmlns='http://www.w3.org/2000/svg', viewBox=f'0 0 {svg_box[0]} {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', id='background', 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 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 states = sorted(states, key=lambda x: -len(x)) @@ -827,24 +840,36 @@ def create_svg(ns, bbox): # Generate SVG, PNG and HTML output files - print('Writing SVG') - with open(ns.output_directory.svg_path, 'wb') as mapfile: + print('Writing Web SVG') + with open(ns.output_directory.svg_web_path, 'wb') as mapfile: root = etree.ElementTree(svg) 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') - 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) print('Writing HTML SVG page') html = etree.Element('html') 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) html.append(head) body = etree.Element('body') 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])) create_imagemap(ns, svg_box, obj, erfas, chaostreffs, texts) body.append(obj) @@ -856,7 +881,7 @@ def create_svg(ns, bbox): print('Writing HTML Image Map') html = etree.Element('html') 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) html.append(head) body = etree.Element('body') @@ -916,7 +941,7 @@ def main(): if os.path.exists(ns.cache_directory.chaostreff_info): os.unlink(ns.cache_directory.chaostreff_info) 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) diff --git a/style/erfamap.css b/style/erfamap.css index bee51f6..aff96de 100644 --- a/style/erfamap.css +++ b/style/erfamap.css @@ -51,13 +51,8 @@ a:hover > text { fill: #5b8ca7; } -@font-face { - font-family: 'concertone'; - src: url('./concertone-regular.ttf'); -} - text.erfalabel { - font-family: concertone; + font-family: "Concert One"; font-size: 45px; z-index: 100; user-select: none;