diff --git a/.github/workflows/build-sr2025.yml b/.github/workflows/build-sr2025.yml
new file mode 100644
index 0000000..b77b501
--- /dev/null
+++ b/.github/workflows/build-sr2025.yml
@@ -0,0 +1,52 @@
+name: Build SR2024 floorplan outputs
+
+on:
+ push:
+ paths:
+ - '2025/**'
+ - '.github/workflows/build-sr2025.yml'
+ workflow_dispatch:
+
+jobs:
+ build-2024:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Set up Python 3.11
+ uses: actions/setup-python@v4
+ with:
+ python-version: "3.11"
+
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install -r 2025/requirements.txt
+
+ sudo apt-get update
+ sudo apt-get install inkscape
+
+ - name: Install fonts
+ run: |
+ sudo mkdir -p /usr/share/fonts/sr-fonts
+
+ for f in resources/*.zip
+ do
+ unzip $f -d /usr/share/fonts/sr-fonts/$(basename ${f/.zip})
+ done
+
+ sudo fc-cache -fv
+
+ - name: Build sr2025 floorplans
+ run: |
+ cd 2025
+ ./generate_svg.py --teams team_names.yaml
+
+ ./populate-map.py
+ inkscape --export-type=pdf,png --export-dpi=192 output/map-with-teams.svg
+
+ - name: Archive floorplan files
+ uses: actions/upload-artifact@v3
+ with:
+ name: "sr2025 floorplans"
+ path: 2025/output
diff --git a/2025/.gitignore b/2025/.gitignore
new file mode 100644
index 0000000..23e8d39
--- /dev/null
+++ b/2025/.gitignore
@@ -0,0 +1,4 @@
+/venv
+
+full plans/
+output/
diff --git a/2025/README.md b/2025/README.md
new file mode 100644
index 0000000..c669e9e
--- /dev/null
+++ b/2025/README.md
@@ -0,0 +1,82 @@
+# Notes for 2025
+
+The templates here are intended to be 100:1 scale diagram of all the areas used in SUSU, they are believed to be within 5% accurate.
+Locations highlighted in red on one of the venue related layers require measurements to be taken to assure their size.
+
+#### Requires:
+- Python 3.6+
+- PyYaml
+
+```bash
+pip3 install --user -U -r requirements.txt
+```
+
+## Layers
+#### Floorplan layers:
+- Notes *(Additional notes)*
+- Dimensions
+ - VenueDimensions *(Dimensions relating to venue floorplan)*
+ - AreaDimensions *(Dimensions relating to area placement and layout)*
+- TLA *(Team names)*
+- Network
+ - Displays *(SRcomp info monitors)*
+ - VenueNetwork *(Venue installed networking)*
+ - NetworkEquipment *(Additional cables, switches, routers and access points)*
+- Power
+ - VenuePower *(Venue power outlets)*
+ - PowerEquipment *(Required power locations)*
+- Areas
+ - AreaTables *(Position of designated tables)*
+ - AreaZones *(Marked out zones)*
+- Shepherding *(Shepherd specific highlighting)*
+- General
+ - GeneralLabels *(global drawing labels)*
+ - VenueFloorplan *(venue floorplans)*
+ - Exits *(Fire exits and lanes)*
+
+
+#### Key Layers:
+- General
+- Network
+- Power
+- Areas
+- Shepherding
+
+
+#### YAML Layer Specification
+```yaml
+image: "cube.svg"
+title: "Cube Layout"
+show:
+ - ALL
+hide:
+ - shepherding
+embed:
+-
+ marker: "KEY__"
+ image: "key.svg"
+ show:
+ - ALL
+ hide:
+ - shepherding
+```
+
+#### JSON Layer Specification
+```json
+{
+ "image": "cube.svg",
+ "title": "Cube Layout",
+ "show": [
+ "All"
+ ],
+ "hide": [
+ "shepherding"
+ ],
+ "embed": [{
+ "marker": "KEY__",
+ "image": "key.svg",
+ "show":[ "ALL" ],
+ "hide": [ "shepherding" ]
+ }]
+}
+```
diff --git a/2025/generate_svg.py b/2025/generate_svg.py
new file mode 100755
index 0000000..c7267c6
--- /dev/null
+++ b/2025/generate_svg.py
@@ -0,0 +1,212 @@
+#!/usr/bin/env python3
+
+import re
+import json
+import argparse
+import xml.etree.ElementTree as ET
+from pathlib import Path
+from contextlib import suppress
+
+import yaml
+
+
+def register_xml_namespaces(xml_file):
+ with open(xml_file, 'r') as file:
+ ns = dict([node for _, node in ET.iterparse(file, events=['start-ns'])])
+
+ for prefix in ns:
+ ET.register_namespace(prefix, ns[prefix])
+
+ return ns
+
+
+def load_config(conf_file):
+ try:
+ with open(conf_file, 'r') as file:
+ conf = json.load(file) # load json specification
+ except json.JSONDecodeError:
+ print(f"Failed to read {conf_file} as JSON, trying YAML")
+ with open(conf_file, 'r') as file:
+ conf = yaml.safe_load(file) # load yaml specification
+
+ return conf
+
+
+def print_layers(
+ root, show=[], hide=[],
+ ns={
+ 'svg': 'http://www.w3.org/2000/svg',
+ 'inkscape': 'http://www.inkscape.org/namespaces/inkscape'
+ },
+ prefix="", autoshow=False
+):
+ if prefix:
+ prefix += '/'
+
+ for child in root.findall('./svg:g[@inkscape:groupmode="layer"]', ns):
+ label = prefix + child.get(f'{{{ns["inkscape"]}}}label', "")
+
+ if autoshow:
+ if label in hide:
+ child.set('style', 'display:none')
+ print(f'Hide: {label}')
+ else:
+ child.attrib.pop('style', None)
+ print(f'Show: {label}')
+ else:
+ if label in hide:
+ child.set('style', 'display:none')
+ print(f'Hide: {label}')
+ elif label in show or 'ALL' in show:
+ child.attrib.pop('style', None)
+ print(f'Show: {label}')
+ child = print_layers(child, show, hide, ns, prefix=label, autoshow=True)
+ else:
+ child.set('style', 'display:none')
+ print(f'Hide: {label}')
+
+ return root
+
+
+def set_titles(root, title, version, scale, ns):
+ with suppress(IndexError, AttributeError, TypeError):
+ root.find('.//svg:text[svg:tspan="{{title}}"]', ns)[0].text = title
+ with suppress(IndexError, AttributeError, TypeError):
+ root.find('.//svg:text[svg:tspan="{{version}}"]', ns)[0].text = f"Version: {version}"
+ with suppress(IndexError, AttributeError, TypeError):
+ root.find('.//svg:text[svg:tspan="{{scale}}" ]', ns)[0].text = f"Scale 1:{scale:.0f}"
+
+ return root
+
+
+def insert_tlas(svg_root, teams, ns):
+ TLA = svg_root.find('svg:g[@inkscape:label="TLA"]', ns) # add team names
+ if TLA:
+ for team in TLA.findall('.//svg:text/svg:tspan', ns):
+ team_no = re.search(r'@T_(\d+)', team.text)
+ if team_no is not None:
+ team.text = teams.get(int(team_no[1]), '')
+
+ return svg_root
+
+
+def embed_svg(embedded, root, ns, template_dir, team_names=None):
+ print(f"Embedding {embedded['image']}")
+ embedded_root = ET.parse(Path(template_dir) / embedded['image']).getroot() # load svg
+
+ if team_names is not None:
+ embedded_root = insert_tlas(embedded_root, team_names, ns)
+
+ # display only selected layers or ALL
+ embedded_root = print_layers(
+ embedded_root,
+ embedded.get('show', ['ALL']),
+ embedded.get('hide', []),
+ ns=ns,
+ )
+
+ embed_marker = embedded['marker']
+ # get container that we will be inserting into
+ embed_parent = root.find(f'.//svg:rect[@id="{embed_marker}"]/..', ns)
+ if embed_parent is None:
+ print(f"Failed to find the marker {embed_marker}")
+ return
+
+ # get the element that will be replaced by the embed
+ embed_child = embed_parent.find(f'./svg:rect[@id="{embed_marker}"]', ns)
+ if embed_child is None:
+ print(f"Failed to find the marker {embed_marker}")
+ return
+
+ embed_index = list(embed_parent).index(embed_child)
+
+ for field in ['x', 'y', 'width', 'height']: # set x, y, height & width from the placeholder
+ embedded_root.set(field, embed_child.get(field))
+
+ embed_parent[embed_index] = embedded_root # replace element with svg
+ print(f"Embedded {embedded['image']}")
+ return root
+
+
+def generate_svg(spec_path, template_dir, out_dir, base_scale, teams_file=None):
+ team_names = None
+ spec = load_config(spec_path)
+
+ svg_file = Path(template_dir) / spec['image']
+ ns = register_xml_namespaces(svg_file)
+
+ root_tree = ET.parse(svg_file) # load svg
+ root = root_tree.getroot()
+
+ root = set_titles(
+ root,
+ spec.get('title', spec['image']),
+ spec.get('version', 0.1),
+ spec.get('scale', 1) * base_scale,
+ ns,
+ )
+
+ if teams_file:
+ team_names = load_config(teams_file)
+ root = insert_tlas(root, team_names, ns)
+
+ # set scale
+ try:
+ old_width = float(root.get('width')[:-2])
+ old_height = float(root.get('height')[:-2])
+ except TypeError:
+ print("Invalid SVG")
+
+ scale = spec.get('scale', 1)
+ root.set('width', str(old_width / scale) + "cm")
+ root.set('height', str(old_height / scale) + "cm")
+
+ # display only selected layers or ALL
+ print_layers(root, spec.get('show', ['ALL']), spec.get('hide', []), ns=ns)
+
+ for embedded in spec.get('embed', []): # add nested svgs (including key)
+ root = embed_svg(embedded, root, ns, template_dir, team_names)
+
+ out_dir = Path(out_dir)
+ out_dir.mkdir(exist_ok=True)
+
+ out_file = spec.get('title', 'output').replace(' ', '_') + '.svg'
+ root_tree.write(out_dir / out_file, xml_declaration=True, encoding='UTF-8')
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('specs', type=Path, nargs=argparse.OPTIONAL, default=Path('layouts/'), help=(
+ "Folder or file containing the YAML/JSON that defines what output SVG's are created, "
+ "defaults to '%(default)s'"
+ ))
+ parser.add_argument('-s', '--base-scale', type=int, default=100, help=(
+ "The initial 1:X scale that the template files are at, defaults to %(default)s"
+ ))
+ parser.add_argument('-t', '--templates', type=Path, default=Path('templates/'), help=(
+ "Folder containing the template SVG's, defaults to '%(default)s'"
+ ))
+ parser.add_argument('--teams', type=Path, default=None, help=(
+ "The YAML/JSON file containing a mapping of number to TLA"
+ ))
+ parser.add_argument('-o', '--output', type=Path, default=Path('output/'), help=(
+ "Folder to store the output SVG's, defaults to '%(default)s'"
+ ))
+
+ args = parser.parse_args()
+
+ if args.specs.is_file():
+ generate_svg(args.specs, args.templates, args.output, args.base_scale, teams_file=args.teams)
+ elif args.specs.is_dir():
+ for spec in args.specs.iterdir():
+ if not spec.is_file():
+ continue
+
+ print(f"Processing spec file {spec}")
+ generate_svg(spec, args.templates, args.output, args.base_scale, teams_file=args.teams)
+ else:
+ print("The specification is neither a file nor directory")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/2025/layouts/cube_layout.yaml b/2025/layouts/cube_layout.yaml
new file mode 100644
index 0000000..78e4fb9
--- /dev/null
+++ b/2025/layouts/cube_layout.yaml
@@ -0,0 +1,15 @@
+image: "cube.svg"
+title: "Cube Layout"
+version: 0.1
+show:
+ - ALL
+hide:
+ - Shepherding
+embed:
+-
+ marker: "KEY__"
+ image: "key.svg"
+ show:
+ - ALL
+ hide:
+ - Shepherding
\ No newline at end of file
diff --git a/2025/layouts/cube_network.yaml b/2025/layouts/cube_network.yaml
new file mode 100644
index 0000000..1e536d4
--- /dev/null
+++ b/2025/layouts/cube_network.yaml
@@ -0,0 +1,17 @@
+image: "cube.svg"
+title: "Cube Network"
+version: 0.1
+show:
+ - General
+ - Network
+ - Dimensions
+ - Areas
+ - Notes
+embed:
+-
+ marker: "KEY__"
+ image: "key.svg"
+ show:
+ - ALL
+ hide:
+ - Shepherding
\ No newline at end of file
diff --git a/2025/layouts/cube_power.yaml b/2025/layouts/cube_power.yaml
new file mode 100644
index 0000000..d03f643
--- /dev/null
+++ b/2025/layouts/cube_power.yaml
@@ -0,0 +1,17 @@
+image: "cube.svg"
+title: "Cube Power"
+version: 0.1
+show:
+ - General
+ - Power
+ - Dimensions
+ - Areas
+ - Notes
+embed:
+-
+ marker: "KEY__"
+ image: "key.svg"
+ show:
+ - ALL
+ hide:
+ - Shepherding
\ No newline at end of file
diff --git a/2025/layouts/l2_layout.yaml b/2025/layouts/l2_layout.yaml
new file mode 100644
index 0000000..5debeec
--- /dev/null
+++ b/2025/layouts/l2_layout.yaml
@@ -0,0 +1,16 @@
+image: "L2.svg"
+title: "Level 2 Layout"
+version: 0.1
+scale: 2.5
+show:
+ - ALL
+hide:
+ - Shepherding
+embed:
+-
+ marker: "KEY__"
+ image: "key.svg"
+ show:
+ - ALL
+ hide:
+ - Shepherding
\ No newline at end of file
diff --git a/2025/layouts/l2_network_power.yaml b/2025/layouts/l2_network_power.yaml
new file mode 100644
index 0000000..5277202
--- /dev/null
+++ b/2025/layouts/l2_network_power.yaml
@@ -0,0 +1,19 @@
+image: "L2.svg"
+title: "Level 2 Power & Network"
+version: 0.1
+scale: 2.5
+show:
+ - General
+ - Power
+ - Network
+ - Dimensions
+ - Areas
+ - Notes
+embed:
+-
+ marker: "KEY__"
+ image: "key.svg"
+ show:
+ - ALL
+ hide:
+ - Shepherding
\ No newline at end of file
diff --git a/2025/layouts/l2_public.yaml b/2025/layouts/l2_public.yaml
new file mode 100644
index 0000000..0cea12c
--- /dev/null
+++ b/2025/layouts/l2_public.yaml
@@ -0,0 +1,15 @@
+image: "L2.svg"
+title: "Level 2"
+version: 0.1
+scale: 2.5
+show:
+ - General
+ - Areas
+ - TLA
+embed:
+-
+ marker: "KEY__"
+ image: "key.svg"
+ show:
+ - General
+ - Areas
\ No newline at end of file
diff --git a/2025/layouts/l3_layout.yaml b/2025/layouts/l3_layout.yaml
new file mode 100644
index 0000000..3c283f4
--- /dev/null
+++ b/2025/layouts/l3_layout.yaml
@@ -0,0 +1,16 @@
+image: "L3.svg"
+title: "Level 3 Layout"
+version: 0.1
+scale: 3
+show:
+ - ALL
+hide:
+ - Shepherding
+embed:
+-
+ marker: "KEY__"
+ image: "key.svg"
+ show:
+ - ALL
+ hide:
+ - Shepherding
\ No newline at end of file
diff --git a/2025/layouts/l3_network.yaml b/2025/layouts/l3_network.yaml
new file mode 100644
index 0000000..bd9a7e2
--- /dev/null
+++ b/2025/layouts/l3_network.yaml
@@ -0,0 +1,19 @@
+image: "L3.svg"
+title: "Level 3 Network"
+version: 0.1
+scale: 3
+show:
+ - General
+ - Network
+ - Dimensions
+ - Areas
+ - Notes
+embed:
+-
+ marker: "KEY__"
+ image: "key.svg"
+ show:
+ - ALL
+ hide:
+ - Shepherding
+ - Power
\ No newline at end of file
diff --git a/2025/layouts/l3_power.yaml b/2025/layouts/l3_power.yaml
new file mode 100644
index 0000000..f07217d
--- /dev/null
+++ b/2025/layouts/l3_power.yaml
@@ -0,0 +1,18 @@
+image: "L3.svg"
+title: "Level 3 Power"
+version: 0.1
+scale: 3
+show:
+ - General
+ - Power
+ - Dimensions
+ - Areas
+ - Notes
+embed:
+-
+ marker: "KEY__"
+ image: "key.svg"
+ show:
+ - ALL
+ hide:
+ - Shepherding
\ No newline at end of file
diff --git a/2025/layouts/l3_public.yaml b/2025/layouts/l3_public.yaml
new file mode 100644
index 0000000..1bcba59
--- /dev/null
+++ b/2025/layouts/l3_public.yaml
@@ -0,0 +1,15 @@
+image: "L3.svg"
+title: "Level 3"
+version: 0.1
+scale: 3
+show:
+ - General
+ - Areas
+ - TLA
+embed:
+-
+ marker: "KEY__"
+ image: "key.svg"
+ show:
+ - General
+ - Areas
\ No newline at end of file
diff --git a/2025/layouts/l4_layout.yaml b/2025/layouts/l4_layout.yaml
new file mode 100644
index 0000000..a8546b5
--- /dev/null
+++ b/2025/layouts/l4_layout.yaml
@@ -0,0 +1,16 @@
+image: "L4.svg"
+title: "Level 4 Layout"
+version: 0.1
+scale: 1.5
+show:
+ - ALL
+hide:
+ - Shepherding
+embed:
+-
+ marker: "KEY__"
+ image: "key.svg"
+ show:
+ - ALL
+ hide:
+ - Shepherding
\ No newline at end of file
diff --git a/2025/layouts/l4_network.yaml b/2025/layouts/l4_network.yaml
new file mode 100644
index 0000000..40a9262
--- /dev/null
+++ b/2025/layouts/l4_network.yaml
@@ -0,0 +1,19 @@
+image: "L4.svg"
+title: "Level 4 Network"
+version: 0.1
+scale: 1.5
+show:
+ - General
+ - Network
+ - Dimensions
+ - Areas
+ - Notes
+embed:
+-
+ marker: "KEY__"
+ image: "key.svg"
+ show:
+ - ALL
+ hide:
+ - Shepherding
+ - Power
\ No newline at end of file
diff --git a/2025/layouts/l4_power.yaml b/2025/layouts/l4_power.yaml
new file mode 100644
index 0000000..eb48ec0
--- /dev/null
+++ b/2025/layouts/l4_power.yaml
@@ -0,0 +1,19 @@
+image: "L4.svg"
+title: "Level 4 Power"
+version: 0.1
+scale: 1.5
+show:
+ - General
+ - Power
+ - Dimensions
+ - Areas
+ - Notes
+embed:
+-
+ marker: "KEY__"
+ image: "key.svg"
+ show:
+ - ALL
+ hide:
+ - Shepherding
+ - Network
\ No newline at end of file
diff --git a/2025/layouts/l4_public.yaml b/2025/layouts/l4_public.yaml
new file mode 100644
index 0000000..f3fc867
--- /dev/null
+++ b/2025/layouts/l4_public.yaml
@@ -0,0 +1,15 @@
+image: "L4.svg"
+title: "Level 4"
+version: 0.1
+scale: 1.5
+show:
+ - General
+ - Areas
+ - TLA
+embed:
+-
+ marker: "KEY__"
+ image: "key.svg"
+ show:
+ - General
+ - Areas
\ No newline at end of file
diff --git a/2025/layouts/shepherding.yaml b/2025/layouts/shepherding.yaml
new file mode 100644
index 0000000..5eaf0bf
--- /dev/null
+++ b/2025/layouts/shepherding.yaml
@@ -0,0 +1,52 @@
+image: "layout.svg"
+title: "Shepherding"
+version: 0.1
+show:
+ - ALL
+embed:
+-
+ marker: "KEY__"
+ image: "key.svg"
+ show:
+ - General
+ - Areas
+ - Shepherding
+-
+ marker: "L2__"
+ image: "L2.svg"
+ show:
+ - General
+ - Areas
+ - TLA
+ - Shepherding
+ hide:
+ - General/GeneralLabels
+-
+ marker: "L3__"
+ image: "L3.svg"
+ show:
+ - General
+ - Areas
+ - TLA
+ - Shepherding
+ hide:
+ - General/GeneralLabels
+-
+ marker: "L4__"
+ image: "L4.svg"
+ show:
+ - General
+ - Areas
+ - TLA
+ - Shepherding
+ hide:
+ - General/GeneralLabels
+-
+ marker: "CUBE__"
+ image: "cube.svg"
+ show:
+ - General
+ - Areas
+ - TLA
+ hide:
+ - General/GeneralLabels
diff --git a/2025/layouts/venue_map.yaml b/2025/layouts/venue_map.yaml
new file mode 100644
index 0000000..f8eeff5
--- /dev/null
+++ b/2025/layouts/venue_map.yaml
@@ -0,0 +1,48 @@
+image: "layout.svg"
+title: "Venue Map"
+version: 0.1
+show:
+ - ALL
+embed:
+-
+ marker: "KEY__"
+ image: "key.svg"
+ show:
+ - General
+ - Areas
+-
+ marker: "L2__"
+ image: "L2.svg"
+ show:
+ - General
+ - Areas
+ - TLA
+ hide:
+ - General/GeneralLabels
+-
+ marker: "L3__"
+ image: "L3.svg"
+ show:
+ - General
+ - Areas
+ - TLA
+ hide:
+ - General/GeneralLabels
+-
+ marker: "L4__"
+ image: "L4.svg"
+ show:
+ - General
+ - Areas
+ - TLA
+ hide:
+ - General/GeneralLabels
+-
+ marker: "CUBE__"
+ image: "cube.svg"
+ show:
+ - General
+ - Areas
+ - TLA
+ hide:
+ - General/GeneralLabels
diff --git a/2025/layouts/venue_tech_map.yaml b/2025/layouts/venue_tech_map.yaml
new file mode 100644
index 0000000..cf249b9
--- /dev/null
+++ b/2025/layouts/venue_tech_map.yaml
@@ -0,0 +1,58 @@
+image: "layout_tech.svg"
+title: "Venue Tech Map"
+version: 0.1
+show:
+ - ALL
+embed:
+-
+ marker: "KEY__"
+ image: "key.svg"
+ show:
+ - General
+ - Areas
+ - Power
+ - Network
+-
+ marker: "L2__"
+ image: "L2.svg"
+ show:
+ - General
+ - Areas
+ - TLA
+ - Power
+ - Network
+ hide:
+ - General/GeneralLabels
+-
+ marker: "L3__"
+ image: "L3.svg"
+ show:
+ - General
+ - Areas
+ - TLA
+ - Power
+ - Network
+ hide:
+ - General/GeneralLabels
+-
+ marker: "L4__"
+ image: "L4.svg"
+ show:
+ - General
+ - Areas
+ - TLA
+ - Power
+ - Network
+ hide:
+ - General/GeneralLabels
+-
+ marker: "CUBE__"
+ image: "cube.svg"
+ show:
+ - General
+ - Areas
+ - TLA
+ - Power
+ - Network
+ hide:
+ - General/GeneralLabels
diff --git a/2025/populate-map.py b/2025/populate-map.py
new file mode 100755
index 0000000..2bde4d6
--- /dev/null
+++ b/2025/populate-map.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python3
+import re
+import argparse
+import xml.etree.ElementTree as ET
+from pathlib import Path
+
+from generate_svg import load_config, register_xml_namespaces
+
+
+def insert_tla_list(svg_root, teams, ns):
+ # Sort teams so the list is alphabetical by TLA
+ teams_alphabetical = sorted(teams.items(), key=lambda x: x[1])
+ teams_alphabetical = (
+ (idx, tla) for idx, tla in teams_alphabetical
+ # remove unused pits, represented with an em dash or hyphen
+ if tla not in ['—', '-']
+ )
+ team_dict = dict(enumerate(teams_alphabetical, start=1))
+
+ TLA = svg_root.find('svg:g[@inkscape:label="TLA"]', ns) # add team names
+ if TLA:
+ for team in TLA.findall('.//svg:text/svg:tspan', ns):
+ if team.text is None:
+ continue
+ team_no = re.search(r'@t_(\d+)', team.text)
+ if team_no is not None:
+ tla_data = team_dict.get(int(team_no[1]))
+ team.text = f"{tla_data[1]}:" if tla_data is not None else ""
+ else:
+ idx_no = re.search(r'@I_(\d+)', team.text)
+ if idx_no is not None:
+ tla_data = team_dict.get(int(idx_no[1]))
+ team.text = str(tla_data[0]) if tla_data is not None else ""
+
+ return svg_root
+
+
+def generate_map_svg(svg_file, out_file, teams_file=None):
+ ns = register_xml_namespaces(svg_file)
+
+ root_tree = ET.parse(svg_file) # load svg
+ root = root_tree.getroot()
+
+ if teams_file:
+ team_names = load_config(teams_file)
+ insert_tla_list(root, team_names, ns)
+
+ root_tree.write(Path(out_file), xml_declaration=True, encoding='UTF-8')
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('-i', '--map', type=Path, default=Path('templates/map.svg'), help=(
+ "The template map SVG, defaults to '%(default)s'"
+ ))
+ parser.add_argument('--teams', type=Path, default=Path('team_names.yaml'), help=(
+ "The YAML/JSON file containing a mapping of number to TLA"
+ ))
+ parser.add_argument('-o', '--output', type=Path, default=Path('output/map-with-teams.svg'), help=(
+ "Filepath of the output SVG, defaults to '%(default)s'"
+ ))
+
+ args = parser.parse_args()
+
+ generate_map_svg(args.map, args.output, teams_file=args.teams)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/2025/requirements.txt b/2025/requirements.txt
new file mode 100644
index 0000000..c3726e8
--- /dev/null
+++ b/2025/requirements.txt
@@ -0,0 +1 @@
+pyyaml
diff --git a/2025/team_names.yaml b/2025/team_names.yaml
new file mode 100644
index 0000000..5b8337b
--- /dev/null
+++ b/2025/team_names.yaml
@@ -0,0 +1,30 @@
+1: 'T01'
+2: 'T02'
+3: 'T03'
+4: 'T04'
+5: 'T05'
+6: 'T06'
+7: 'T07'
+8: 'T08'
+9: 'T09'
+10: 'T10'
+11: 'T11'
+12: 'T12'
+13: 'T13'
+14: 'T14'
+15: 'T15'
+16: 'T16'
+17: 'T17'
+18: 'T18'
+19: 'T19'
+20: 'T20'
+21: 'T21'
+22: 'T22'
+23: 'T23'
+24: 'T24'
+25: 'T25'
+26: 'T26'
+27: 'T27'
+28: 'T28'
+29: 'T29'
+30: 'T30'
diff --git a/2025/templates/L2.svg b/2025/templates/L2.svg
new file mode 100755
index 0000000..04264cc
--- /dev/null
+++ b/2025/templates/L2.svg
@@ -0,0 +1,2029 @@
+
+
+
+
diff --git a/2025/templates/L3.svg b/2025/templates/L3.svg
new file mode 100755
index 0000000..740c6f1
--- /dev/null
+++ b/2025/templates/L3.svg
@@ -0,0 +1,3342 @@
+
+
+
+
diff --git a/2025/templates/L4.svg b/2025/templates/L4.svg
new file mode 100755
index 0000000..6d8305f
--- /dev/null
+++ b/2025/templates/L4.svg
@@ -0,0 +1,1190 @@
+
+
+
+
diff --git a/2025/templates/cube.svg b/2025/templates/cube.svg
new file mode 100755
index 0000000..da14ae3
--- /dev/null
+++ b/2025/templates/cube.svg
@@ -0,0 +1,1300 @@
+
+
+
+
diff --git a/2025/templates/key.svg b/2025/templates/key.svg
new file mode 100755
index 0000000..1d50faa
--- /dev/null
+++ b/2025/templates/key.svg
@@ -0,0 +1,328 @@
+
+
+
+
diff --git a/2025/templates/layout.svg b/2025/templates/layout.svg
new file mode 100644
index 0000000..3a70325
--- /dev/null
+++ b/2025/templates/layout.svg
@@ -0,0 +1,194 @@
+
+
+
+
diff --git a/2025/templates/layout_tech.svg b/2025/templates/layout_tech.svg
new file mode 100644
index 0000000..a52ed25
--- /dev/null
+++ b/2025/templates/layout_tech.svg
@@ -0,0 +1,194 @@
+
+
+
+
diff --git a/2025/templates/map.svg b/2025/templates/map.svg
new file mode 100644
index 0000000..a3fe896
--- /dev/null
+++ b/2025/templates/map.svg
@@ -0,0 +1,9986 @@
+
+
+
+