Add clickable SVG links
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
parent
7c954242eb
commit
2610a54fe1
4 changed files with 89 additions and 21 deletions
File diff suppressed because one or more lines are too long
|
@ -1 +1 @@
|
||||||
{"Erlangen": [11.0028028, 49.6000372], "Paderborn": [8.7479251, 51.7171873], "Hamburg": [9.9443486, 53.5583644], "Aachen": [6.091774700480718, 50.7715109], "Basel": [7.6342977, 47.5323068], "Berlin": [13.38283, 52.5217046], "Bremen": [8.7879452, 53.0864586], "K\u00f6ln": [6.912896, 50.9505492], "Darmstadt": [8.6511275, 49.8708409], "Dresden": [13.7288095, 51.0811269], "D\u00fcsseldorf": [6.7996122, 51.2125738], "Frankfurt am Main": [8.6362035, 50.1241797], "Freiburg": [7.8405746483681735, 47.99297755], "G\u00f6ttingen": [9.9446185, 51.5453725], "Hannover": [9.717936908887404, 52.38812135], "Karlsruhe": [8.4073787, 49.0066019], "Mannheim": [8.4886818, 49.4636517], "M\u00fcnchen": [11.5607949, 48.153629], "Salzburg": [13.0561199, 47.7939873], "Stuttgart": [9.1800132, 48.7784485], "Ulm": [9.9910884, 48.4005863], "Wien": [16.35611792351422, 48.209451099999995], "Wiesbaden": [8.2295012, 50.0831784], "Z\u00fcrich": [8.5203159, 47.3869751], "Siegen": [8.0044503, 50.8689203], "Kaiserslautern": [7.762277299724986, 49.44049735], "Essen": [7.024639594585695, 51.43860565], "Dortmund": [7.464966783180763, 51.52768425], "Fulda": [9.6775152, 50.5588931], "W\u00fcrzburg": [9.923678752181619, 49.80223915], "Bamberg": [10.89268892402827, 49.90189135], "Kassel": [9.484939, 51.3183203]}
|
{"Erlangen": {"location": [11.0028028, 49.6000372], "web": "https://erlangen.ccc.de/", "name": "Bits'n'Bugs"}, "Paderborn": {"location": [8.7479251, 51.7171873], "web": "https://c3pb.de/", "name": "Chaos Computer Club Paderborn"}, "Hamburg": {"location": [9.9443486, 53.5583644], "web": "https://hamburg.ccc.de/", "name": "Chaos Computer Club Hamburg"}, "Aachen": {"location": [6.091774700480718, 50.7715109], "web": "https://aachen.ccc.de/", "name": "Chaos Computer Club Aachen"}, "Basel": {"location": [7.6342977, 47.5323068], "web": "https://www.ccc-basel.ch/", "name": "Chaos Computer Club Basel"}, "Berlin": {"location": [13.38283, 52.5217046], "web": "https://berlin.ccc.de/", "name": "Chaos Computer Club Berlin"}, "Bremen": {"location": [8.7879452, 53.0864586], "web": "https://ccchb.de/", "name": "Chaos Computer Club Bremen"}, "K\u00f6ln": {"location": [6.912896, 50.9505492], "web": "https://koeln.ccc.de/", "name": "Chaos Computer Club Cologne"}, "Darmstadt": {"location": [8.6511275, 49.8708409], "web": "https://www.chaos-darmstadt.de/", "name": "Chaos Computer Club Darmstadt"}, "Dresden": {"location": [13.7288095, 51.0811269], "web": "https://c3d2.de/", "name": "Chaos Computer Club Dresden"}, "D\u00fcsseldorf": {"location": [6.7996122, 51.2125738], "web": "https://chaosdorf.de/", "name": "Chaos Computer Club D\u00fcsseldorf"}, "Frankfurt am Main": {"location": [8.6362035, 50.1241797], "web": "https://ccc-ffm.de/", "name": "Chaos Computer Club Frankfurt"}, "Freiburg": {"location": [7.8405746483681735, 47.99297755], "web": "https://cccfr.de/", "name": "Chaos Computer Club Freiburg"}, "G\u00f6ttingen": {"location": [9.9446185, 51.5453725], "web": "https://cccgoe.de/", "name": "Chaos Computer Club G\u00f6ttingen"}, "Hannover": {"location": [9.717936908887404, 52.38812135], "web": "https://hannover.ccc.de/", "name": "Chaos Computer Club Hannover"}, "Karlsruhe": {"location": [8.4073787, 49.0066019], "web": "https://entropia.de/", "name": "Chaos Computer Club Karlsruhe"}, "Mannheim": {"location": [8.4886818, 49.4636517], "web": "https://www.ccc-mannheim.de/", "name": "Chaos Computer Club Mannheim"}, "M\u00fcnchen": {"location": [11.5607949, 48.153629], "web": "https://www.muc.ccc.de/", "name": "Chaos Computer Club M\u00fcnchen"}, "Salzburg": {"location": [13.0561199, 47.7939873], "web": "https://cccsbg.at", "name": "Chaos Computer Club Salzburg"}, "Stuttgart": {"location": [9.1800132, 48.7784485], "web": "https://cccs.de/", "name": "Chaos Computer Club Stuttgart"}, "Ulm": {"location": [9.9910884, 48.4005863], "web": "https://ulm.ccc.de/", "name": "Chaos Computer Club Ulm"}, "Wien": {"location": [16.35611792351422, 48.209451099999995], "web": "https://c3w.at/", "name": "Chaos Computer Club Wien"}, "Wiesbaden": {"location": [8.2295012, 50.0831784], "web": "https://cccwi.de/", "name": "Chaos Computer Club Wiesbaden"}, "Z\u00fcrich": {"location": [8.5203159, 47.3869751], "web": "https://www.ccczh.ch/", "name": "Chaos Computer Club Z\u00fcrich"}, "Siegen": {"location": [8.0044503, 50.8689203], "web": "https://chaos-siegen.de/", "name": "Chaos Computer Club Siegen"}, "Kaiserslautern": {"location": [7.762277299724986, 49.44049735], "web": "http://www.chaos-inkl.de", "name": "Chaos inKL."}, "Essen": {"location": [7.024639594585695, 51.43860565], "web": "https://chaospott.de/"}, "Dortmund": {"location": [7.464966783180763, 51.52768425], "web": "https://www.chaostreff-dortmund.de/", "name": "Chaostreff Dortmund"}, "L\u00fcbeck": {"location": [10.6713232, 53.8687751], "web": "https://chaotikum.org/", "name": "Chaotikum"}, "Fulda": {"location": [9.6775152, 50.5588931], "web": "https://maglab.space/", "name": "Magrathea Laboratoratories"}, "W\u00fcrzburg": {"location": [9.923678752181619, 49.80223915], "web": "https://nerd2nerd.org/", "name": "Nerd2Nerd"}, "Bamberg": {"location": [10.89268892402827, 49.90189135], "web": "https://www.hackerspace-bamberg.de/", "name": "backspace"}, "Kassel": {"location": [9.484939, 51.3183203], "web": "https://flipdot.org/", "name": "flipdot"}}
|
|
@ -37,9 +37,9 @@ class CachePaths:
|
||||||
self.chaostreff_info = os.path.join(path, 'chaostreff-info.json')
|
self.chaostreff_info = os.path.join(path, 'chaostreff-info.json')
|
||||||
|
|
||||||
|
|
||||||
ERFA_URL = 'https://doku.ccc.de/Spezial:Semantische_Suche/format%3Djson/limit%3D50/link%3Dall/headers%3Dshow/searchlabel%3DJSON/class%3Dsortable-20wikitable-20smwtable/sort%3D/order%3Dasc/offset%3D0/-5B-5BKategorie:Erfa-2DKreise-5D-5D-20-5B-5BChaostreff-2DActive::wahr-5D-5D/-3FChaostreff-2DCity/-3FChaostreff-2DPhysical-2DAddress/-3FChaostreff-2DPhysical-2DHousenumber/-3FChaostreff-2DPhysical-2DPostcode/-3FChaostreff-2DPhysical-2DCity/-3FChaostreff-2DCountry/mainlabel%3D/prettyprint%3Dtrue/unescape%3Dtrue'
|
ERFA_URL = 'https://doku.ccc.de/Spezial:Semantische_Suche/format%3Djson/limit%3D50/link%3Dall/headers%3Dshow/searchlabel%3DJSON/class%3Dsortable-20wikitable-20smwtable/sort%3D/order%3Dasc/offset%3D0/-5B-5BKategorie:Erfa-2DKreise-5D-5D-20-5B-5BChaostreff-2DActive::wahr-5D-5D/-3FChaostreff-2DCity/-3FChaostreff-2DPhysical-2DAddress/-3FChaostreff-2DPhysical-2DHousenumber/-3FChaostreff-2DPhysical-2DPostcode/-3FChaostreff-2DPhysical-2DCity/-3FChaostreff-2DCountry/-3FPublic-2DWeb/-3FChaostreff-2DLongname/mainlabel%3D/prettyprint%3Dtrue/unescape%3Dtrue'
|
||||||
|
|
||||||
CHAOSTREFF_URL = 'https://doku.ccc.de/Spezial:Semantische_Suche/format%3Djson/limit%3D50/link%3Dall/headers%3Dshow/searchlabel%3DJSON/class%3Dsortable-20wikitable-20smwtable/sort%3D/order%3Dasc/offset%3D0/-5B-5BKategorie:Chaostreffs-5D-5D-20-5B-5BChaostreff-2DActive::wahr-5D-5D/-3FChaostreff-2DCity/-3FChaostreff-2DPhysical-2DAddress/-3FChaostreff-2DPhysical-2DHousenumber/-3FChaostreff-2DPhysical-2DPostcode/-3FChaostreff-2DPhysical-2DCity/-3FChaostreff-2DCountry/mainlabel%3D/prettyprint%3Dtrue/unescape%3Dtrue'
|
CHAOSTREFF_URL = 'https://doku.ccc.de/Spezial:Semantische_Suche/format%3Djson/limit%3D50/link%3Dall/headers%3Dshow/searchlabel%3DJSON/class%3Dsortable-20wikitable-20smwtable/sort%3D/order%3Dasc/offset%3D0/-5B-5BKategorie:Chaostreffs-5D-5D-20-5B-5BChaostreff-2DActive::wahr-5D-5D/-3FChaostreff-2DCity/-3FChaostreff-2DPhysical-2DAddress/-3FChaostreff-2DPhysical-2DHousenumber/-3FChaostreff-2DPhysical-2DPostcode/-3FChaostreff-2DPhysical-2DCity/-3FChaostreff-2DCountry/-3FPublic-2DWeb/-3FChaostreff-2DLongname/mainlabel%3D/prettyprint%3Dtrue/unescape%3Dtrue'
|
||||||
|
|
||||||
|
|
||||||
def sparql_query(query):
|
def sparql_query(query):
|
||||||
|
@ -205,7 +205,12 @@ def fetch_erfas(target, url):
|
||||||
if location is None:
|
if location is None:
|
||||||
print(f'WARNING: No location for {name}')
|
print(f'WARNING: No location for {name}')
|
||||||
city = erfa['printouts']['Chaostreff-City'][0]
|
city = erfa['printouts']['Chaostreff-City'][0]
|
||||||
erfas[city] = location
|
erfas[city] = {'location': location}
|
||||||
|
if len(erfa['printouts']['Public-Web']) > 0:
|
||||||
|
erfas[city]['web'] = erfa['printouts']['Public-Web'][0]
|
||||||
|
if len(erfa['printouts']['Chaostreff-Longname']) > 0:
|
||||||
|
erfas[city]['name'] = erfa['printouts']['Chaostreff-Longname'][0]
|
||||||
|
|
||||||
with open(target, 'w') as f:
|
with open(target, 'w') as f:
|
||||||
json.dump(erfas, f)
|
json.dump(erfas, f)
|
||||||
|
|
||||||
|
@ -222,7 +227,10 @@ def compute_bbox(ns):
|
||||||
for path in tqdm.tqdm([ns.cache_directory.erfa_info, ns.cache_directory.chaostreff_info]):
|
for path in tqdm.tqdm([ns.cache_directory.erfa_info, ns.cache_directory.chaostreff_info]):
|
||||||
with open(path, 'r') as f:
|
with open(path, 'r') as f:
|
||||||
erfadata = json.load(f)
|
erfadata = json.load(f)
|
||||||
for lon, lat in erfadata.values():
|
for data in erfadata.values():
|
||||||
|
if 'location' not in data:
|
||||||
|
continue
|
||||||
|
lon, lat = data['location']
|
||||||
if len(bounds) == 0:
|
if len(bounds) == 0:
|
||||||
bounds.append(lon)
|
bounds.append(lon)
|
||||||
bounds.append(lat)
|
bounds.append(lat)
|
||||||
|
@ -561,18 +569,30 @@ def create_svg(ns, bbox):
|
||||||
shapes_countries.append((name, ts))
|
shapes_countries.append((name, ts))
|
||||||
|
|
||||||
erfas = {}
|
erfas = {}
|
||||||
|
erfa_urls = {}
|
||||||
|
erfa_names = {}
|
||||||
with open(ns.cache_directory.erfa_info, 'r') as f:
|
with open(ns.cache_directory.erfa_info, 'r') as f:
|
||||||
ctdata = json.load(f)
|
ctdata = json.load(f)
|
||||||
for city, location in ctdata.items():
|
for city, data in ctdata.items():
|
||||||
|
location = data.get('location')
|
||||||
if location is None:
|
if location is None:
|
||||||
continue
|
continue
|
||||||
xt, yt = transformer.transform(*location)
|
xt, yt = transformer.transform(*location)
|
||||||
erfas[city] = (xt*scalex - origin[0], origin[1] - yt*scaley)
|
erfas[city] = (xt*scalex - origin[0], origin[1] - yt*scaley)
|
||||||
|
web = data.get('web')
|
||||||
|
if web is not None:
|
||||||
|
erfa_urls[city] = web
|
||||||
|
name = data.get('name')
|
||||||
|
if name is not None:
|
||||||
|
erfa_names[city] = name
|
||||||
|
|
||||||
chaostreffs = {}
|
chaostreffs = {}
|
||||||
|
chaostreff_urls = {}
|
||||||
|
chaostreff_names = {}
|
||||||
with open(ns.cache_directory.chaostreff_info, 'r') as f:
|
with open(ns.cache_directory.chaostreff_info, 'r') as f:
|
||||||
ctdata = json.load(f)
|
ctdata = json.load(f)
|
||||||
for city, location in ctdata.items():
|
for city, data in ctdata.items():
|
||||||
|
location = data.get('location')
|
||||||
if location is None:
|
if location is None:
|
||||||
continue
|
continue
|
||||||
if city in erfas:
|
if city in erfas:
|
||||||
|
@ -582,7 +602,12 @@ def create_svg(ns, bbox):
|
||||||
continue
|
continue
|
||||||
xt, yt = transformer.transform(*location)
|
xt, yt = transformer.transform(*location)
|
||||||
chaostreffs[city] = (xt*scalex - origin[0], origin[1] - yt*scaley)
|
chaostreffs[city] = (xt*scalex - origin[0], origin[1] - yt*scaley)
|
||||||
|
web = data.get('web')
|
||||||
|
if web is not None:
|
||||||
|
chaostreff_urls[city] = web
|
||||||
|
name = data.get('name')
|
||||||
|
if name is not None:
|
||||||
|
chaostreff_names[city] = name
|
||||||
|
|
||||||
rectbox = [0, 0, svg_box[0], svg_box[1]]
|
rectbox = [0, 0, svg_box[0], svg_box[1]]
|
||||||
for name, shape in shapes_states + shapes_countries:
|
for name, shape in shapes_states + shapes_countries:
|
||||||
|
@ -598,6 +623,9 @@ def create_svg(ns, bbox):
|
||||||
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]))
|
||||||
|
|
||||||
|
print('Layouting labels')
|
||||||
|
texts = optimize_text_layout(ns, erfas, chaostreffs, (int(svg_box[0]), int(svg_box[1])), svg)
|
||||||
|
|
||||||
defs = etree.Element('defs')
|
defs = etree.Element('defs')
|
||||||
style = etree.Element('style', type='text/css')
|
style = etree.Element('style', type='text/css')
|
||||||
style.text = f'@import url({ns.stylesheet})'
|
style.text = f'@import url({ns.stylesheet})'
|
||||||
|
@ -629,26 +657,41 @@ def create_svg(ns, bbox):
|
||||||
svg.append(poly)
|
svg.append(poly)
|
||||||
|
|
||||||
for city, location in erfas.items():
|
for city, location in erfas.items():
|
||||||
|
box = texts[city]
|
||||||
|
if city in erfa_urls:
|
||||||
|
group = etree.Element('a', href=erfa_urls[city], target='_blank')
|
||||||
|
else:
|
||||||
|
group = etree.Element('g')
|
||||||
|
group.set('data-erfa', city)
|
||||||
circle = etree.Element('circle', cx=str(location[0]), cy=str(location[1]), r=str(ns.dotsize_erfa))
|
circle = etree.Element('circle', cx=str(location[0]), cy=str(location[1]), r=str(ns.dotsize_erfa))
|
||||||
circle.set('class', 'erfa')
|
circle.set('class', 'erfa')
|
||||||
circle.set('data-erfa', city)
|
circle.set('data-erfa', city)
|
||||||
svg.append(circle)
|
if city in erfa_names:
|
||||||
|
title = etree.Element('title')
|
||||||
|
title.text = erfa_names[city]
|
||||||
|
circle.append(title)
|
||||||
|
group.append(circle)
|
||||||
|
text = etree.Element('text', x=str(box.left), y=str(box.top + box.meta['baseline']))
|
||||||
|
text.set('class', 'erfalabel')
|
||||||
|
text.set('data-erfa', city)
|
||||||
|
text.text = box.meta['text']
|
||||||
|
group.append(text)
|
||||||
|
svg.append(group)
|
||||||
|
|
||||||
for city, location in chaostreffs.items():
|
for city, location in chaostreffs.items():
|
||||||
circle = etree.Element('circle', cx=str(location[0]), cy=str(location[1]), r=str(ns.dotsize_treff))
|
circle = etree.Element('circle', cx=str(location[0]), cy=str(location[1]), r=str(ns.dotsize_treff))
|
||||||
circle.set('class', 'chaostreff')
|
circle.set('class', 'chaostreff')
|
||||||
circle.set('data-chaostreff', city)
|
circle.set('data-chaostreff', city)
|
||||||
svg.append(circle)
|
if city in chaostreff_names:
|
||||||
|
title = etree.Element('title')
|
||||||
|
title.text = chaostreff_names[city]
|
||||||
print('Layouting labels')
|
circle.append(title)
|
||||||
texts = optimize_text_layout(ns, erfas, chaostreffs, (int(svg_box[0]), int(svg_box[1])), svg)
|
if city in chaostreff_urls:
|
||||||
for city, box in texts.items():
|
a = etree.Element('a', href=chaostreff_urls[city], target='_blank', title='foox')
|
||||||
text = etree.Element('text', x=str(box.left), y=str(box.top + box.meta['baseline']))
|
a.append(circle)
|
||||||
text.set('class', 'erfalabel')
|
svg.append(a)
|
||||||
text.set('data-erfa', city)
|
else:
|
||||||
text.text = box.meta['text']
|
svg.append(circle)
|
||||||
svg.append(text)
|
|
||||||
|
|
||||||
print('Done, writing SVG')
|
print('Done, writing SVG')
|
||||||
with open('map.svg', 'wb') as mapfile:
|
with open('map.svg', 'wb') as mapfile:
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
rect.background {
|
rect.background {
|
||||||
fill: #759eb5;
|
fill: #759eb5;
|
||||||
stroke: none;
|
stroke: none;
|
||||||
|
@ -16,12 +18,32 @@ polygon.state {
|
||||||
stroke-width: 3;
|
stroke-width: 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
circle.erfa {
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
circle.chaostreff {
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
circle.erfa, circle.chaostreff {
|
circle.erfa, circle.chaostreff {
|
||||||
fill: #f47e1e;
|
fill: #f47e1e;
|
||||||
stroke: #ffffff;
|
stroke: #ffffff;
|
||||||
stroke-width: 3;
|
stroke-width: 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a > circle, a > text {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover > circle {
|
||||||
|
fill: #5b8ca7;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover > text {
|
||||||
|
fill: #5b8ca7;
|
||||||
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'concertone';
|
font-family: 'concertone';
|
||||||
src: url('./concertone-regular.ttf');
|
src: url('./concertone-regular.ttf');
|
||||||
|
@ -30,16 +52,19 @@ circle.erfa, circle.chaostreff {
|
||||||
text.erfalabel {
|
text.erfalabel {
|
||||||
font-family: concertone;
|
font-family: concertone;
|
||||||
font-size: 45px;
|
font-size: 45px;
|
||||||
|
z-index: 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
rect.debugleft {
|
rect.debugleft {
|
||||||
stroke: red;
|
stroke: red;
|
||||||
stroke-width: 1;
|
stroke-width: 1;
|
||||||
fill: none;
|
fill: none;
|
||||||
|
z-index: 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
rect.debugright {
|
rect.debugright {
|
||||||
stroke: green;
|
stroke: green;
|
||||||
stroke-width: 1;
|
stroke-width: 1;
|
||||||
fill: none;
|
fill: none;
|
||||||
|
z-index: 1000;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue