Skip to content

Stadium Design Tool

The Stadium Design Tool is a specialized admin interface for creating and configuring stadium seat maps. This is a complex graphical editor that enables stadium operators to define the complete seating layout including sectors, rows, individual seats, aisles, and special areas.

Overview

Stadium seat map design involves multiple layers of complexity:

  1. Stadium Outline - The overall shape and boundaries
  2. Sector Definition - Major sections of the stadium (stands, tribunes)
  3. Block/Sub-sector Layout - Divisions within sectors
  4. Row Configuration - Individual rows with varying seat counts
  5. Seat Placement - Individual seat positions with coordinates
  6. Special Areas - Aisles, stairs, technical positions, accessibility
  7. Pricing Zones - Mapping seats to price categories
  8. Visual Representation - SVG paths and coordinates for rendering
┌─────────────────────────────────────────────────────────────────────────────┐
│                         Stadium Design Architecture                          │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  ┌─────────────────────────────────────────────────────────────────────┐    │
│  │                        Stadium Container                             │    │
│  │  ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐        │    │
│  │  │    Sector A     │ │    Sector B     │ │    Sector C     │        │    │
│  │  │  ┌───────────┐  │ │  ┌───────────┐  │ │  ┌───────────┐  │        │    │
│  │  │  │  Block 1  │  │ │  │  Block 1  │  │ │  │  Block 1  │  │        │    │
│  │  │  │ ┌───────┐ │  │ │  │ ┌───────┐ │  │ │  │ ┌───────┐ │  │        │    │
│  │  │  │ │Row 1  │ │  │ │  │ │Row 1  │ │  │ │  │ │Row 1  │ │  │        │    │
│  │  │  │ │1 2 3 4│ │  │ │  │ │1 2 3 4│ │  │ │  │ │1 2 3 4│ │  │        │    │
│  │  │  │ └───────┘ │  │ │  │ └───────┘ │  │ │  │ └───────┘ │  │        │    │
│  │  │  │ ┌───────┐ │  │ │  │ ┌───────┐ │  │ │  │ ┌───────┐ │  │        │    │
│  │  │  │ │Row 2  │ │  │ │  │ │Row 2  │ │  │ │  │ │Row 2  │ │  │        │    │
│  │  │  │ │1 2 3 4│ │  │ │  │ │1 2 3 4│ │  │ │  │ │1 2 3 4│ │  │        │    │
│  │  │  │ └───────┘ │  │ │  │ └───────┘ │  │ │  │ └───────┘ │  │        │    │
│  │  │  └───────────┘  │ │  └───────────┘  │ │  └───────────┘  │        │    │
│  │  └─────────────────┘ └─────────────────┘ └─────────────────┘        │    │
│  └─────────────────────────────────────────────────────────────────────┘    │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

Design Tool Interface

Main Layout

┌─────────────────────────────────────────────────────────────────────────────┐
│  Stadium Design Tool - Maksimir Stadium                    [Save] [Preview] │
├─────────────────────────────────────────────────────────────────────────────┤
│ ┌──────────┐ ┌────────────────────────────────────────────┐ ┌─────────────┐ │
│ │ Toolbox  │ │                                            │ │ Properties  │ │
│ │ ──────── │ │                                            │ │ ────────── │ │
│ │          │ │                                            │ │             │ │
│ │ [Select] │ │                                            │ │ Selection:  │ │
│ │ [Pan]    │ │           Canvas / Design Area             │ │ Sector A    │ │
│ │ [Zoom]   │ │                                            │ │             │ │
│ │ ──────── │ │              (SVG Editor)                  │ │ Name: _____ │ │
│ │ [Sector] │ │                                            │ │ Code: _____ │ │
│ │ [Block]  │ │                                            │ │ Price Zone: │ │
│ │ [Row]    │ │                                            │ │             │ │
│ │ [Seat]   │ │                                            │ │             │ │
│ │ ──────── │ │                                            │ │ Rows: [+]   │ │
│ │ [Aisle]  │ │                                            │ │ - Row 1 (30)│ │
│ │ [Stairs] │ │                                            │ │ - Row 2 (32)│ │
│ │ [Gate]   │ │                                            │ │ - Row 3 (32)│ │
│ │ ──────── │ │                                            │ │             │ │
│ │ [Import] │ │                                            │ │ [Configure] │ │
│ │ [Export] │ │                                            │ │ [Delete]    │ │
│ └──────────┘ └────────────────────────────────────────────┘ └─────────────┘ │
│ ┌───────────────────────────────────────────────────────────────────────────┤
│ │ Layers: [Stadium] [Sectors] [Rows] [Seats] [Annotations]    Zoom: 100%  │ │
│ └───────────────────────────────────────────────────────────────────────────┘
└─────────────────────────────────────────────────────────────────────────────┘

Tool Modes

Tool Function Keyboard
Select Select and move elements (button shows text label "Select") V
Draw Draw/place seats on canvas (button shows text label "Draw") D
Erase Remove seats from canvas (button shows text label "Erase") E
Fill Fill rectangular area with seats (button shows text label "Fill") F
Pan Pan the canvas view Space+Drag
Zoom Zoom in/out Scroll / Z
Sector Draw sector polygon S
Block Draw block within sector B
Row Define row within block R
Seat Place individual seats T
Aisle Mark aisle/walkway A
Stairs Mark stairway areas W
Gate Mark entry/exit gates G

Tool Button Labels

The primary editing tools (Select, Draw, Erase, Fill) display text labels on their buttons for clarity. This helps administrators quickly identify tools without relying solely on icons.

Capacity Enforcement

When editing a seat map within a sector, the editor enforces the sector's configured capacity limit:

  • The current seat count and sector capacity are displayed in the editor toolbar
  • When the seat count reaches the sector capacity, the Draw and Fill tools are disabled
  • Attempting to add seats beyond the limit shows a warning notification
  • Administrators must increase the sector capacity or remove existing seats before adding more

Design Workflow

Phase 1: Stadium Outline

Purpose: Define the overall stadium boundary and orientation.

Steps:

  1. Create New Stadium or Import Base Template
  2. New: Start with empty canvas
  3. Import: Load from SVG, DXF (CAD), or existing template

  4. Define Stadium Boundary

  5. Draw outer polygon of stadium
  6. Set stadium orientation (north arrow)
  7. Define field/pitch area (rectangular)
  8. Set coordinate system origin

  9. Set Global Properties

  10. Stadium name and code
  11. City and country
  12. Stadium type (Football, Multi-purpose)
  13. Total capacity (read-only, auto-calculated from sector seat counts)
┌─────────────────────────────────────────────────────────────────┐
│  Stadium Properties                                              │
├─────────────────────────────────────────────────────────────────┤
│  Name:        [Stadion Maksimir                    ]            │
│  Code:        [MAKSIMIR    ]                                    │
│  City:        [Zagreb      ]  Country: [Croatia     ▼]          │
│  Type:        [● Football  ○ Multi-purpose]                     │
│  Orientation: [North ▼]                                         │
│                                                                  │
│  Dimensions:                                                     │
│  Length:      [200] m       Width: [180] m                      │
│  Field:       [105 x 68] m                                      │
│                                                                  │
│  Coordinate System:                                              │
│  Origin:      [Center of Field ▼]                               │
│  Units:       [Meters ▼]                                        │
│  Scale:       [1:100]                                           │
└─────────────────────────────────────────────────────────────────┘

Phase 2: Sector Definition

Purpose: Divide the stadium into major sections (stands/tribunes).

Sector Types:

Type Description Example
Stand Main seating tribune North Stand, South Stand
Corner Corner section NE Corner, SW Corner
Premium VIP/Premium area Executive Box, Skybox
General General admission Standing area
Away Visiting team supporters Away Section
Technical Non-public area Press, Camera, Technical

Drawing Sectors:

  1. Select Sector Tool (S)
  2. Click to place polygon vertices
  3. Double-click or press Enter to complete
  4. Assign sector properties in Properties panel

Sector Properties:

┌─────────────────────────────────────────────────────────────────┐
│  Sector Properties                                               │
├─────────────────────────────────────────────────────────────────┤
│  Basic Information                                               │
│  ─────────────────                                              │
│  Name:           [North Stand - Section A          ]            │
│  Code:           [ISTOK-A    ]  (unique identifier)             │
│  Type:           [Stand              ▼]                         │
│  Access Level:   [Public             ▼]                         │
│                                                                  │
│  Geometry                                                        │
│  ─────────────────                                              │
│  Shape:          Polygon (5 vertices)                           │
│  Area:           1,250 m²                                       │
│  Orientation:    Facing South (toward field)                    │
│                                                                  │
│  Calculated Capacity (read-only)                                 │
│  ─────────────────                                              │
│  Total Seats:     0 (auto-calculated from seat map)             │
│  ℹ Capacity updates automatically as seats are added/removed.   │
│                                                                  │
│  Pricing                                                         │
│  ─────────────────                                              │
│  Default Zone:   [Category A (Premium)  ▼]                      │
│                                                                  │
│  Entry Points                                                    │
│  ─────────────────                                              │
│  Gates:          [Gate 1, Gate 2]  [Configure...]               │
│                                                                  │
│  [Delete Sector]                    [Apply Changes]             │
└─────────────────────────────────────────────────────────────────┘

Phase 3: Block/Sub-sector Configuration

Purpose: Divide sectors into manageable blocks (often separated by aisles).

Large sectors are typically divided into blocks for: - Easier navigation - Separate entry points - Different pricing within same stand - Structural divisions (stairs, columns)

Block Definition:

┌─────────────────────────────────────────────────────────────────┐
│  Block Configuration - North Stand Section A                     │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  ┌─────────┐     ┌─────────┐     ┌─────────┐                    │
│  │ Block 1 │     │ Block 2 │     │ Block 3 │                    │
│  │         │     │         │     │         │                    │
│  │  Rows   │ ║ ║ │  Rows   │ ║ ║ │  Rows   │                    │
│  │  1-15   │Aisle│  1-15   │Aisle│  1-15   │                    │
│  │         │     │         │     │         │                    │
│  └─────────┘     └─────────┘     └─────────┘                    │
│                                                                  │
│  Blocks in this sector:                                          │
│  ┌────┬──────────────┬──────┬───────────┬─────────┐             │
│  │ #  │ Name         │ Rows │ Seats/Row │ Total   │             │
│  ├────┼──────────────┼──────┼───────────┼─────────┤             │
│  │ 1  │ Block A1     │ 15   │ 28-32     │ 450     │             │
│  │ 2  │ Block A2     │ 15   │ 30-34     │ 480     │             │
│  │ 3  │ Block A3     │ 15   │ 28-32     │ 450     │             │
│  └────┴──────────────┴──────┴───────────┴─────────┘             │
│                                                                  │
│  Total Sector Capacity: 1,380 seats                              │
│                                                                  │
│  [Add Block]  [Auto-Divide...]  [Import Layout...]              │
└─────────────────────────────────────────────────────────────────┘

Phase 4: Row Configuration

Purpose: Define rows within each block with seat counts and numbering.

Row Properties:

Property Description Example
Identifier Row name/number "1", "A", "AA"
Seat Count Number of seats in row 32
Start Number First seat number 1
Direction Numbering direction LTR, RTL
Curve Radius For curved rows 150m
Row Spacing Distance to next row 0.8m
Seat Width Standard seat width 0.45m

Row Configuration Interface:

┌─────────────────────────────────────────────────────────────────┐
│  Row Configuration - Block A1                                    │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  Generation Method:                                              │
│  ○ Manual (define each row individually)                        │
│  ● Automatic (define pattern, system generates)                 │
│  ○ Import (from CSV/spreadsheet)                                │
│                                                                  │
│  ═══════════════════════════════════════════════════════════    │
│                                                                  │
│  Automatic Generation Settings:                                  │
│  ─────────────────────────────                                  │
│  Number of Rows:    [15    ]                                    │
│  Row Naming:        [● Numeric (1,2,3)  ○ Alpha (A,B,C)]        │
│  Start From:        [1     ]                                    │
│                                                                  │
│  Seat Count Pattern:                                             │
│  [● Fixed count    ] [32   ] seats per row                      │
│  [○ Variable       ] First row: [28] Last row: [36]             │
│  [○ Custom pattern ] [28,30,32,32,34,34,36,36,36,36...]        │
│                                                                  │
│  Numbering Direction:                                            │
│  [● Left-to-Right (1,2,3...)]                                   │
│  [○ Right-to-Left (...3,2,1)]                                   │
│  [○ Center-Out (odd left, even right)]                          │
│                                                                  │
│  Geometry:                                                       │
│  Row Spacing:       [0.85  ] m                                  │
│  Seat Width:        [0.45  ] m                                  │
│  Row Curve:         [● Straight  ○ Curved, radius: [___] m]     │
│                                                                  │
│  Preview:                                                        │
│  ─────────────────────────────                                  │
│  Row 1:  [1][2][3]...[30][31][32]    (32 seats)                 │
│  Row 2:  [1][2][3]...[30][31][32]    (32 seats)                 │
│  Row 3:  [1][2][3]...[30][31][32]    (32 seats)                 │
│  ...                                                             │
│  Row 15: [1][2][3]...[30][31][32]    (32 seats)                 │
│                                                                  │
│  Total: 480 seats                                                │
│                                                                  │
│  [Cancel]                              [Generate Rows]          │
└─────────────────────────────────────────────────────────────────┘

Complex Row Patterns:

Stadiums often have non-uniform seating:

Example: Curved Stand with Variable Row Lengths

Row 1 (closest to field):  |1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|  (20 seats)
Row 2:                     |1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|  (22 seats)
Row 3:                     |1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|  (24 seats)
...
Row 15 (top):              |1|2|3|4|5|...|30|31|32|33|34|35|36|37|38|39|40|  (40 seats)

Phase 5: Individual Seat Placement

Purpose: Define precise seat coordinates for visualization and generate seat records.

Coordinate System:

┌─────────────────────────────────────────────────────────────────┐
│  Seat Coordinate System                                          │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  Global Stadium Coordinates:                                     │
│  - Origin at field center (0,0)                                 │
│  - X-axis: West (-) to East (+)                                 │
│  - Y-axis: South (-) to North (+)                               │
│  - Units: Meters                                                │
│                                                                  │
│  Sector Local Coordinates:                                       │
│  - Origin at sector's reference point (usually bottom-left)     │
│  - Relative positioning within sector                           │
│                                                                  │
│  Seat Position Data:                                             │
│  - global_x, global_y: Stadium coordinates                      │
│  - local_x, local_y: Sector-relative coordinates                │
│  - svg_x, svg_y: SVG viewport coordinates (for rendering)       │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

Automatic Seat Placement:

System calculates seat positions based on: - Row geometry (straight or curved) - Seat width and spacing - Row starting position - Numbering direction

// Seat Position Calculation (pseudocode)
function calculateSeatPositions(row, sectorGeometry) {
  const seats = [];
  const rowCurve = row.curveRadius || Infinity;
  const seatWidth = row.seatWidth || 0.45;

  for (let i = 0; i < row.seatCount; i++) {
    const seatNumber = row.direction === 'RTL'
      ? row.startNumber + row.seatCount - 1 - i
      : row.startNumber + i;

    // Calculate position along row
    const positionAlongRow = i * seatWidth;

    // Apply curve if specified
    let x, y;
    if (rowCurve === Infinity) {
      // Straight row
      x = row.startX + positionAlongRow;
      y = row.startY;
    } else {
      // Curved row - calculate arc position
      const angle = positionAlongRow / rowCurve;
      x = row.centerX + rowCurve * Math.sin(angle);
      y = row.centerY + rowCurve * Math.cos(angle);
    }

    seats.push({
      row: row.identifier,
      number: seatNumber,
      x: x,
      y: y,
      type: 'standard'
    });
  }

  return seats;
}

Manual Seat Adjustment:

For irregular layouts, seats can be manually positioned:

┌─────────────────────────────────────────────────────────────────┐
│  Seat Position Editor                                            │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  Selected Seat: Row 5, Seat 12                                  │
│                                                                  │
│  Position:                                                       │
│  X: [45.23    ] m    Y: [12.67    ] m                           │
│                                                                  │
│  Adjust:                                                         │
│  [←] [-0.1m] [+0.1m] [→]                                        │
│  [↑] [-0.1m] [+0.1m] [↓]                                        │
│                                                                  │
│  Snap to Grid: [✓]   Grid Size: [0.05] m                        │
│                                                                  │
│  Seat Properties:                                                │
│  Type: [Standard ▼]                                             │
│  - Standard                                                      │
│  - Technical (not for sale)                                     │
│  - Accessibility (wheelchair)                                    │
│  - Companion (next to accessibility)                            │
│  - Restricted View                                               │
│  - Premium/VIP                                                   │
│                                                                  │
│  [Apply to Selection]                                           │
└─────────────────────────────────────────────────────────────────┘

Phase 6: Special Areas & Obstacles

Purpose: Mark non-seating areas that affect layout.

Area Types:

Type Symbol Description
Aisle Walkway between seat blocks
Stairs Stairway access
Column Structural pillar (obstructs view)
Gate ⊏⊐ Entry/exit point
Camera Camera position (technical)
Railing Safety barrier
Emergency Exit Emergency egress

Aisle Configuration:

┌─────────────────────────────────────────────────────────────────┐
│  Aisle/Walkway Configuration                                     │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  Type: [Vertical Aisle (between blocks) ▼]                      │
│                                                                  │
│  Position:                                                       │
│  Between: [Block A1 ▼] and [Block A2 ▼]                         │
│  Width:   [1.2   ] m                                            │
│                                                                  │
│  Extends:                                                        │
│  From Row: [1     ] To Row: [15    ]                            │
│                                                                  │
│  Impact on Seating:                                              │
│  [✓] Removes seats from adjacent rows                           │
│  [✓] Creates seat numbering gap                                 │
│                                                                  │
│  Visual:                                                         │
│  Row 5:  |1|2|3|...|28| |AISLE| |1|2|3|...|30|                  │
│          Block A1               Block A2                         │
│                                                                  │
│  [Cancel]                                [Apply]                 │
└─────────────────────────────────────────────────────────────────┘

Seat Gap Handling:

When aisles or obstacles interrupt rows:

Example: Row with center aisle

Row 5: |1|2|3|4|5|6|7|8|9|10|11|12|13|14| GAP |15|16|17|18|19|20|21|22|23|24|25|26|27|28|

Gap Configuration:
- After seat: 14
- Gap width: 2 seat positions
- Continues from: 15 (maintains sequence)

Alternative: Split numbering
- Left section: 1-14
- Right section: 1-14 (separate)

Phase 7: Seat Type Classification

Purpose: Mark seats with special characteristics.

Seat Types:

Type Code Color Description
Standard STD None Regular seat
Technical TECH Purple Press, camera, broadcast
Accessibility ACC Blue Wheelchair position
Companion COMP Light Blue Adjacent to accessibility
Restricted View RV Orange Obstructed sightline
Premium PREM Gold Enhanced comfort/service
Segregated SEG Red Away fans section

Bulk Classification Interface:

┌─────────────────────────────────────────────────────────────────┐
│  Bulk Seat Classification                                        │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  Selection Method:                                               │
│  ○ By Position (click seats on map)                             │
│  ● By Range (specify row/seat range)                            │
│  ○ By Pattern (every Nth seat)                                  │
│                                                                  │
│  Range Selection:                                                │
│  Sector:    [North Stand - A ▼]                                 │
│  Block:     [Block A1         ▼]                                │
│  Rows:      [1    ] to [3    ]                                  │
│  Seats:     [1    ] to [4    ]                                  │
│                                                                  │
│  Selection Preview: 12 seats                                     │
│  Row 1: Seats 1-4                                               │
│  Row 2: Seats 1-4                                               │
│  Row 3: Seats 1-4                                               │
│                                                                  │
│  Assign Type: [Accessibility (wheelchair) ▼]                    │
│                                                                  │
│  Additional Settings:                                            │
│  [✓] Mark adjacent seats as Companion                           │
│  [✓] Add to accessibility inventory                             │
│                                                                  │
│  [Cancel]                             [Apply Classification]    │
└─────────────────────────────────────────────────────────────────┘

Phase 8: Pricing Zones

Purpose: Map seats to price categories.

Zone Definition:

┌─────────────────────────────────────────────────────────────────┐
│  Pricing Zone Configuration                                      │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  Available Price Zones:                                          │
│  ┌──────┬──────────────────┬─────────┬─────────────────────────┐│
│  │ Zone │ Name             │ Color   │ Default Price           ││
│  ├──────┼──────────────────┼─────────┼─────────────────────────┤│
│  │ A    │ Premium          │ Gold    │ €100                    ││
│  │ B    │ Category 1       │ Blue    │ €75                     ││
│  │ C    │ Category 2       │ Green   │ €50                     ││
│  │ D    │ Category 3       │ Yellow  │ €35                     ││
│  │ E    │ Category 4       │ Orange  │ €20                     ││
│  └──────┴──────────────────┴─────────┴─────────────────────────┘│
│                                                                  │
│  Zone Assignment:                                                │
│  ─────────────────                                              │
│  Method: [● By Sector  ○ By Block  ○ By Row Range  ○ Custom]   │
│                                                                  │
│  Sector Mapping:                                                 │
│  ┌──────────────────────────┬───────────────┐                   │
│  │ Sector                   │ Zone          │                   │
│  ├──────────────────────────┼───────────────┤                   │
│  │ West Stand - VIP         │ [A - Premium ▼]                   │
│  │ West Stand - Upper       │ [B - Cat 1   ▼]                   │
│  │ East Stand - Lower       │ [C - Cat 2   ▼]                   │
│  │ North Stand              │ [D - Cat 3   ▼]                   │
│  │ South Stand (Away)       │ [E - Cat 4   ▼]                   │
│  └──────────────────────────┴───────────────┘                   │
│                                                                  │
│  [Preview on Map]                            [Apply Zones]      │
└─────────────────────────────────────────────────────────────────┘

Import & Export

Import Formats

Format Use Case Description
SVG Visual template Stadium outline and sector shapes
DXF/DWG CAD import Architectural drawings
CSV Seat data Bulk import of seat coordinates
JSON Full export Complete stadium configuration
GeoJSON Geographic Stadium with real-world coordinates

CSV Import Format

For importing seat data from spreadsheets:

sector_code,block_code,row,seat_number,type,x,y,price_zone
ISTOK-A,A1,1,1,standard,45.23,12.67,B
ISTOK-A,A1,1,2,standard,45.68,12.67,B
ISTOK-A,A1,1,3,standard,46.13,12.67,B
ISTOK-A,A1,1,4,accessibility,46.58,12.67,B
ISTOK-A,A1,1,5,companion,47.03,12.67,B

JSON Export Format

Complete stadium configuration:

{
  "stadium": {
    "id": "maksimir",
    "name": "Stadion Maksimir",
    "code": "MAKSIMIR",
    "city": "Zagreb",
    "country": "HR",
    "capacity": 35000,  // Auto-calculated from sector seat counts (read-only)
    "coordinates": {
      "lat": 45.8186,
      "lng": 16.0186
    }
  },
  "geometry": {
    "viewBox": "0 0 1000 800",
    "fieldRect": {
      "x": 200,
      "y": 150,
      "width": 600,
      "height": 400
    }
  },
  "sectors": [
    {
      "id": "sector-a",
      "code": "ISTOK-A",
      "name": "East Stand - Section A",
      "type": "stand",
      "path": "M100,100 L200,100 L200,250 L100,250 Z",
      "priceZone": "B",
      "blocks": [
        {
          "id": "block-a1",
          "code": "A1",
          "rows": [
            {
              "identifier": "1",
              "seatCount": 32,
              "startNumber": 1,
              "direction": "LTR",
              "curveRadius": null,
              "y": 12.67
            }
          ]
        }
      ]
    }
  ],
  "seats": [
    {
      "id": "seat-istok-a-1-1",
      "sector": "ISTOK-A",
      "block": "A1",
      "row": "1",
      "number": "1",
      "type": "standard",
      "priceZone": "B",
      "coordinates": {
        "x": 45.23,
        "y": 12.67,
        "svgX": 452.3,
        "svgY": 126.7
      }
    }
  ],
  "specialAreas": [
    {
      "type": "aisle",
      "path": "M150,100 L150,250",
      "width": 1.2
    },
    {
      "type": "gate",
      "id": "gate-1",
      "position": { "x": 100, "y": 175 },
      "name": "Gate 1"
    }
  ],
  "metadata": {
    "createdAt": "2025-01-15T10:00:00Z",
    "createdBy": "admin@hns.hr",
    "version": "1.2.0",
    "lastModified": "2025-11-20T14:30:00Z"
  }
}

Validation & Preview

Validation Rules

Rule Severity Description
Unique seat IDs Error Each seat must have unique sector/row/number
Row continuity Warning Gaps in row numbering detected
Capacity mismatch Warning Calculated capacity differs from target
Overlapping seats Error Seat coordinates overlap
Missing coordinates Error Seats without position data
Orphan blocks Warning Blocks not assigned to sectors
Price zone coverage Warning Seats without price zone

Validation Interface

┌─────────────────────────────────────────────────────────────────┐
│  Stadium Validation Report                                       │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  ✓ Stadium configuration validated                              │
│                                                                  │
│  Summary:                                                        │
│  - Sectors: 12 defined                                          │
│  - Blocks: 36 defined                                           │
│  - Rows: 540 defined                                            │
│  - Seats: 34,872 generated                                      │
│                                                                  │
│  ══════════════════════════════════════════════════════════     │
│                                                                  │
│  ⚠ Warnings (3)                                                 │
│  ├─ Sector ISTOK-C: Calculated capacity (1,420) differs from   │
│  │  target (1,500) by 80 seats                                  │
│  ├─ Block B2, Row 7: Gap in seat numbering (14→16)             │
│  └─ 23 seats have no price zone assigned                        │
│                                                                  │
│  ✗ Errors (1)                                                   │
│  └─ Duplicate seat: ISTOK-A, Row 5, Seat 12 appears twice      │
│                                                                  │
│  [Show on Map]  [Export Report]  [Fix Issues]                   │
└─────────────────────────────────────────────────────────────────┘

Preview Mode

Test stadium visualization before publishing:

┌─────────────────────────────────────────────────────────────────┐
│  Preview Mode - Maksimir Stadium                    [Exit Preview]│
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  View As:                                                        │
│  [● Admin (seat operations)]                                    │
│  [○ Customer (ticket purchase)]                                 │
│  [○ Quota Holder (allocation view)]                             │
│                                                                  │
│  Test Scenarios:                                                 │
│  [✓] Show seat status colors                                    │
│  [✓] Enable seat selection                                      │
│  [✓] Display hover tooltips                                     │
│  [✓] Test zoom/pan navigation                                   │
│                                                                  │
│  Simulate Status Distribution:                                   │
│  Available: [60 ]%  Sold: [30 ]%  Blocked: [10 ]%              │
│  [Apply Distribution]                                            │
│                                                                  │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │                                                          │    │
│  │                Stadium Preview Area                      │    │
│  │              (Interactive visualization)                 │    │
│  │                                                          │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                  │
│  Performance:                                                    │
│  - Seats rendered: 34,872                                       │
│  - Render time: 245ms                                           │
│  - Memory usage: 12.4 MB                                        │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

Versioning & History

Version Control

Stadium configurations support versioning for: - Tracking changes over time - Rolling back problematic changes - Comparing configurations between matches

┌─────────────────────────────────────────────────────────────────┐
│  Version History - Maksimir Stadium                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  Current Version: 1.2.0 (Published)                             │
│                                                                  │
│  Version History:                                                │
│  ┌─────────┬────────────────────┬─────────────┬───────────────┐ │
│  │ Version │ Date               │ Author      │ Changes       │ │
│  ├─────────┼────────────────────┼─────────────┼───────────────┤ │
│  │ 1.2.0   │ 2025-11-20 14:30   │ admin@hns   │ Added Block D4│ │
│  │ 1.1.0   │ 2025-09-15 10:00   │ stadium@hns │ Price zones   │ │
│  │ 1.0.0   │ 2025-06-01 09:00   │ admin@hns   │ Initial setup │ │
│  └─────────┴────────────────────┴─────────────┴───────────────┘ │
│                                                                  │
│  [View Diff]  [Restore Version]  [Create New Version]           │
└─────────────────────────────────────────────────────────────────┘

Data Model

Database Schema

-- Stadium Template
CREATE TABLE stadium (
    id UUID PRIMARY KEY,
    code VARCHAR(20) UNIQUE NOT NULL,
    name VARCHAR(100) NOT NULL,
    city VARCHAR(50),
    country CHAR(2),
    type VARCHAR(20) DEFAULT 'football',
    base_capacity INTEGER,  -- DEPRECATED: Auto-calculated as sum of sector capacities (read-only)
    svg_template TEXT,  -- Base SVG content
    config_json JSONB,  -- Full configuration
    version VARCHAR(20) DEFAULT '1.0.0',
    status VARCHAR(20) DEFAULT 'draft',  -- draft, published, archived
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

-- Sector Definition
CREATE TABLE stadium_sector (
    id UUID PRIMARY KEY,
    stadium_id UUID REFERENCES stadium(id),
    code VARCHAR(20) NOT NULL,
    name VARCHAR(100),
    type VARCHAR(20),  -- stand, corner, premium, etc.
    svg_path TEXT,      -- SVG path definition
    price_zone_id UUID,
    target_capacity INTEGER,  -- Computed from seat count when seat map exists
    display_order INTEGER,
    created_at TIMESTAMP DEFAULT NOW(),
    UNIQUE(stadium_id, code)
);

-- Block Definition
CREATE TABLE stadium_block (
    id UUID PRIMARY KEY,
    sector_id UUID REFERENCES stadium_sector(id),
    code VARCHAR(20) NOT NULL,
    name VARCHAR(100),
    display_order INTEGER,
    created_at TIMESTAMP DEFAULT NOW(),
    UNIQUE(sector_id, code)
);

-- Row Configuration
CREATE TABLE stadium_row_config (
    id UUID PRIMARY KEY,
    block_id UUID REFERENCES stadium_block(id),
    row_identifier VARCHAR(10) NOT NULL,
    seat_count INTEGER NOT NULL,
    start_seat_number INTEGER DEFAULT 1,
    numbering_direction VARCHAR(10) DEFAULT 'LTR',
    curve_radius DECIMAL(10,2),  -- NULL for straight rows
    row_y_position DECIMAL(10,4),
    seat_width DECIMAL(5,3) DEFAULT 0.45,
    row_order INTEGER,
    created_at TIMESTAMP DEFAULT NOW(),
    UNIQUE(block_id, row_identifier)
);

-- Seat Template (generated from row config)
CREATE TABLE stadium_seat_template (
    id UUID PRIMARY KEY,
    sector_id UUID REFERENCES stadium_sector(id),
    block_id UUID REFERENCES stadium_block(id),
    row_identifier VARCHAR(10) NOT NULL,
    seat_number VARCHAR(10) NOT NULL,
    seat_type VARCHAR(20) DEFAULT 'standard',
    price_zone_id UUID,
    x_coordinate DECIMAL(10,4),
    y_coordinate DECIMAL(10,4),
    svg_x DECIMAL(10,4),
    svg_y DECIMAL(10,4),
    created_at TIMESTAMP DEFAULT NOW(),
    UNIQUE(sector_id, row_identifier, seat_number)
);

-- Special Areas
CREATE TABLE stadium_special_area (
    id UUID PRIMARY KEY,
    stadium_id UUID REFERENCES stadium(id),
    area_type VARCHAR(20),  -- aisle, stairs, gate, column
    name VARCHAR(50),
    svg_path TEXT,
    properties JSONB,
    created_at TIMESTAMP DEFAULT NOW()
);

-- Price Zones
CREATE TABLE price_zone (
    id UUID PRIMARY KEY,
    stadium_id UUID REFERENCES stadium(id),
    code VARCHAR(10) NOT NULL,
    name VARCHAR(50),
    color VARCHAR(7),  -- Hex color
    default_price DECIMAL(10,2),
    display_order INTEGER,
    UNIQUE(stadium_id, code)
);


Technology Decisions

Based on comprehensive technology research, the following stack has been selected for implementing the Stadium Design Tool.

Component Technology Rationale
Rendering Canvas (not SVG) Required for 35,000-50,000 seat performance
Library Konva.js Best balance of features, performance, and ease of use
Language TypeScript Type safety for complex editor state
State Management Immer + Custom Immutable updates, undo/redo support
Build Tool Vite Fast development, single bundle output
Integration Standalone SPA Clean separation from Symfony backend
Framework None (vanilla) Minimal overhead, maximum flexibility

Why Konva.js?

  1. Performance - Handles 35,000-50,000 seats with scene graph optimization
  2. Built-in Features - Zoom, pan, drag-drop, selection all native
  3. Official Example - Seats Reservation Widget
  4. Easy Integration - Simple script embed in Twig templates
  5. Active Community - ~10K GitHub stars, regular updates
  6. JSON Serialization - Native save/load for stadium configurations

Performance Requirements Met

Seat Count SVG Canvas (Konva.js) WebGL (PixiJS)
< 2,000 Good Good Overkill
2,000-10,000 Degraded Good Good
10,000-35,000 Poor Good Excellent
35,000-50,000 Unusable Good (Optimized) Excellent
50,000+ N/A Virtual rendering Best

Architecture Overview

┌─────────────────────────────────────────────────────────────┐
│                 HNS Admin Portal (Symfony)                   │
├─────────────────────────────────────────────────────────────┤
│  ┌─────────────────────────────────────────────────────┐    │
│  │  Stadium Design Tool (Standalone SPA)               │    │
│  │  ┌─────────────────────────────────────────────┐    │    │
│  │  │  Konva.js Canvas                            │    │    │
│  │  │  - Stage (container)                        │    │    │
│  │  │  - Layers (sectors, seats, annotations)     │    │    │
│  │  │  - Groups (blocks, rows)                    │    │    │
│  │  │  - Shapes (seats, aisles, gates)           │    │    │
│  │  └─────────────────────────────────────────────┘    │    │
│  │                                                      │    │
│  │  State Management (TypeScript + Immer)              │    │
│  │  - Editor state (zoom, pan, tool mode)              │    │
│  │  - Stadium data (sectors, blocks, rows, seats)      │    │
│  │  - Undo/redo history stack                          │    │
│  │  - Selection state                                  │    │
│  └─────────────────────────────────────────────────────┘    │
│                          │                                   │
│                          ▼ JSON API                          │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  Symfony Backend                                     │    │
│  │  POST /api/stadiums/{id}                            │    │
│  │  GET  /api/stadiums/{id}                            │    │
│  │  POST /api/stadiums/{id}/validate                   │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘

Build vs Buy Decision

Decision: Full Custom Build

Aspect Commercial (Seats.io) Custom Build (Recommended)
Seat Selection UI Excellent Build needed
Stadium Design Tool Basic Full control
50K+ Seat Performance Proven With Konva.js
OIB/Croatian Integration Not available Custom
Cost (5 years) ~$150-300K ~$400-600K
Time to Deploy 2-4 weeks 3-6 months

Commercial solutions excel at seat selection but provide limited seat map design tools. None offer the sophisticated stadium template creation workflow required for HNS.

Fallback Option: PixiJS

If performance testing shows Konva.js struggling with 50K seats:

Aspect Konva.js PixiJS
Rendering Canvas 2D WebGL
Performance Good (50K) Excellent (100K+)
Drawing tools Native Need custom
Learning curve Easy Hard
Development time Shorter Longer

Recommendation: Start with Konva.js, migrate to PixiJS only if needed.

Detailed Technical Specification

For complete technical details including code examples, data structures, and implementation guidance, see Stadium Design Tool Technical Specification.


Last Updated: January 2026