LoveofSportsLLC / NFL

NFL + AI
https://loveoffootball.io/
MIT License
0 stars 0 forks source link

Real-Time Game Momentum/Opportunity Overlay #83

Open zepor opened 1 month ago

zepor commented 1 month ago

Why: An overlayt that visualizes the momentum swings during a game, based on factors like score changes, turnovers, and key plays, spacing, and matchups. This helps fans and analysts understand the flow of the game and identify critical turning points

Title/Concept Name: Real-Time Game Momentum Chart

User Story: As a user (e.g., coach, analyst, fan), I want to visualize the momentum swings during a game to understand the flow and identify critical turning points. This visualization should focus on finding opportunities to create momentum by identifying favorable matchups, coverage mismatches, or weak spots in the defense. The chart should be draggable and resizable within the dashboard.

Acceptance Criteria: • The line chart must visualize momentum swings during a game based on factors like score changes, turnovers, key plays, favorable matchups, and defensive weak spots. • The chart should update in real-time without user intervention. • Users should be able to filter and customize metrics and compare multiple games. • The chart must include hover effects, click-to-expand details, and dynamic filtering options. • The chart should be responsive and support both light and dark themes. • The chart must be draggable and resizable within a dashboard. • Integration with the Sports Radar API for real-time data retrieval.

User Flow:

  1. Data Initialization: ○ User lands on the Dashboard and can either drag it over from the library, or see it turned on or off in a field view of the game.1 ○ The frontend sends a request to the backend to fetch game data using the game ID as a parameter.
  2. Rendering Chart: ○ The momentum chart component initializes and inserts an SVG into the DOM. ○ Data fetched from the backend API is processed and bound to the chart using D3.js.
  3. Interaction: ○ Users see a line chart with time on the X-axis and momentum on the Y-axis. ○ Hovering over a point on the chart displays a tooltip with detailed information. ○ Clicking on a point opens a modal with more detailed play information. ○ Users can drag and resize the chart within the dashboard.
  4. Real-Time Updates: ○ The chart updates dynamically as new data comes in from the backend.
  5. Filtering and Customization: ○ Users can apply filters to view specific metrics, favorable matchups, or coverage mismatches. ○ Users can switch between light and dark themes for better visibility. ○ Users can expand the chart to show more details or reduce it to a summary view to take up less space.

    Axes and Labels: X-Axis (Time): • Game time (e.g., "12:34 2Q" indicating 12 minutes and 34 seconds into the second quarter). Y-Axis (Momentum): • Momentum value (e.g., "75" indicating a high level of game momentum). Data Points Required: • Game Event Data: ○ Time: Specific time of the event within the game. ○ Momentum: Calculated momentum value at that moment. ○ Score: The current score of the game in the format "home_score-away_score". ○ Play Description: Detailed description of the play. ○ Key Player: Player(s) involved in the play. ○ Game Context: Additional context such as down, distance, red zone, etc. Game Data Retrieval via Sports Radar API:

    const fetchGameData = async (gameId) => {
    const response = await fetch(`https://api.sportradar.us/nfl/official/trial/v7/en/games/${gameId}/timeline.json?api_key=YOUR_API_KEY`);
    const data = await response.json();
    return data.timeline.map(event => ({
    time: event.clock,
    momentum: event.momentum,
    score: `${event.home_points}-${event.away_points}`,
    play: event.description,
    keyPlayer: event.keyPlayer,
    gameContext: {
      down: event.down,
      distance: event.distance,
      redZone: event.redZone
    }
    }));
    };

    Example Code Snippets: Chart Initialization and Hover Effects:

    import React, { useEffect, useState, useRef } from 'react';
    import * as d3 from 'd3';
    import axios from 'axios';
    import { Responsive, WidthProvider } from 'react-grid-layout';
    const ResponsiveGridLayout = WidthProvider(Responsive);
    const GameMomentumChart = ({ gameId }) => {
    const chartRef = useRef();
    const [data, setData] = useState([]);
    const [modalOpen, setModalOpen] = useState(false);
    const [selectedPoint, setSelectedPoint] = useState(null);
      useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await axios.get(`/api/game/${gameId}`);
        setData(response.data);
      } catch (error) {
        console.error('Error fetching game data:', error);
      }
    };
        fetchData();
    }, [gameId]);
      useEffect(() => {
    if (data.length) {
      const svg = d3.select(chartRef.current);
      const width = svg.node().getBoundingClientRect().width;
      const height = svg.node().getBoundingClientRect().height;
      const margin = { top: 50, right: 25, bottom: 50, left: 100 };
          svg.attr('width', width + margin.left + margin.right)
        .attr('height', height + margin.top + margin.bottom)
        .append('g')
        .attr('transform', `translate(${margin.left},${margin.top})`);
          const times = data.map(d => d.time);
      const momentum = data.map(d => d.momentum);
      const colorScale = d3.scaleSequential(d3.interpolateYlGnBu).domain([0, 100]);
          const x = d3.scaleBand().range([0, width]).domain(times).padding(0.01);
      const y = d3.scaleLinear().range([height, 0]).domain([0, 100]);
          svg.append('g').call(d3.axisBottom(x)).attr('transform', `translate(0,${height})`);
      svg.append('g').call(d3.axisLeft(y));
          svg.selectAll('circle')
        .data(data)
        .enter()
        .append('circle')
        .attr('cx', d => x(d.time))
        .attr('cy', d => y(d.momentum))
        .attr('r', 5)
        .style('fill', d => colorScale(d.momentum))
        .on('mouseover', function (event, d) {
          d3.select(this).style('opacity', 0.8);
          tooltip.style('opacity', 1)
            .html(`<strong>${d.time}</strong><br/>Momentum: ${d.momentum}<br/>Score: ${d.score}<br/>Play: ${d.play}`)
            .style('left', `${event.pageX + 10}px`)
            .style('top', `${event.pageY + 10}px`);
        })
        .on('mouseleave', function () {
          d3.select(this).style('opacity', 1);
          tooltip.style('opacity', 0);
        })
        .on('click', function (event, d) {
          setSelectedPoint(d);
          setModalOpen(true);
        });
          const tooltip = d3.select('body').append('div')
        .attr('class', 'tooltip')
        .style('position', 'absolute')
        .style('background', 'rgba(0, 0, 0, 0.7)')
        .style('color', '#fff')
        .style('padding', '5px')
        .style('border-radius', '3px')
        .style('opacity', 0);
          return () => svg.selectAll('*').remove(); // Clean up
    }
    }, [data]);
      const layout = [
    { i: 'momentumChart', x: 0, y: 0, w: 2, h: 2 }
    ];
      return (
    <ResponsiveGridLayout className="layout" layouts={{ lg: layout }} breakpoints={{ lg: 1200 }} cols={{ lg: 12 }}>
      <div key="momentumChart">
        <svg ref={chartRef}></svg>
        <div className="tooltip"></div>
        {modalOpen && selectedPoint && (
          <div className="modal">
            <h2>Play Details</h2>
            <p><strong>Time:</strong> {selectedPoint.time}</p>
            <p><strong>Momentum:</strong> {selectedPoint.momentum}</p>
            <p><strong>Score:</strong> {selectedPoint.score}</p>
            <p><strong>Key Play:</strong> {selectedPoint.play}</p>
            <button onClick={() => setModalOpen(false)}>Close</button>
          </div>
        )}
      </div>
    </ResponsiveGridLayout>
    );
    };
    export default GameMomentumChart;

image

itHub Issue for Real-Time Game Momentum Chart:

Implement Real-Time Game Momentum Chart

Description: Create a real-time game momentum chart using React and D3.js/Plotly that visualizes momentum swings during a game, based on factors like score changes, turnovers, key plays, favorable matchups, and defensive weak spots. The chart should be draggable and resizable within a dashboard. Tasks:

@autopilot

codeautopilot[bot] commented 1 month ago

Your organization has reached the subscribed usage limit. You can upgrade your account by purchasing a subscription at Stripe payment link