Exemple #1
0
def lower_defensive_box_mark(full_surf = True, rotate = False,
                             rotation_dir = 'ccw'):
    """
    Generate the dataframe for the points that comprise the bounding box of the
    lower defensive box tick marks as in the court diagram on page 8 of the NBA
    rule book
    
    Parameters
    ----------
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature
    
    Returns
    -------
    lower_defensive_box_mark_dict: a dictionary with the two hash marks that
        comprise the lower defensive box hash marks
    """
    
    # The defensive hash marks are 13' (interior) from the end line, is 2"
    # wide, and 6" long
    lower_defensive_box_mark_1 = create.rectangle(
        x_min = -34, x_max = -34 + (2/12),
        y_min = -5, y_max = -4.5
    )
    
    lower_defensive_box_mark_2 = create.rectangle(
        x_min = -34, x_max = -34 + (2/12),
        y_min = 4.5, y_max = 5
    )
    
    # Reflect the x coordinates over the y axis
    if full_surf:
        lower_defensive_box_mark_1 = lower_defensive_box_mark_1.append(
            transform.reflect(lower_defensive_box_mark_1, over_y = True)
        )
        
        lower_defensive_box_mark_2 = lower_defensive_box_mark_2.append(
            transform.reflect(lower_defensive_box_mark_2, over_y = True)
        )
    
    # Rotate the coordinates if necessary
    if rotate:
        lower_defensive_box_mark_1 = transform.rotate(
            lower_defensive_box_mark_1,
            rotation_dir
        )
        
        lower_defensive_box_mark_2 = transform.rotate(
            lower_defensive_box_mark_2,
            rotation_dir
        )
        
    lower_defensive_box_mark_dict = {
        'lower_defensive_box_mark_1': lower_defensive_box_mark_1,
        'lower_defensive_box_mark_2': lower_defensive_box_mark_2
    }
    
    return lower_defensive_box_mark_dict
Exemple #2
0
def faceoff_lines(
        center=(0, 0), full_surf=True, rotate=False, rotation_dir='ccw'):
    """
    Generate the dataframe for the points that comprise the face-off spot's
    hash marks as specified in Rule 1.9 of the NHL rule book
    
    Parameters
    ----------
    center: a tuple containing the center coordinates of the spot to be drawn
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature
    
    Returns
    -------
    faceoff_line_dict: a dictionary with keys of 'center' and 'faceoff_lines',
        where 'center' corresponds to the center coordinates of the face-off
        spot, and 'faceoff_lines' is a pandas dataframe of coordinates needed
        to plot the hashmarks around a face-off spot
    """
    faceoff_line = pd.DataFrame({
        'x': [-2, -6, -6, -2 - (2 / 12), -2 - (2 / 12), -2, -2],
        'y': [.75, .75, .75 + (2 / 12), .75 + (2 / 12), 3.75, 3.75, .75]
    })

    # At each face-off spot, there are four of these lines. Now that one is
    # created, reflect over the x axis to get a second, then reflect both of
    # those sets of lines over the y axis to get the remaining two
    faceoff_line = faceoff_line.append(
        transform.reflect(faceoff_line, over_y=True))
    faceoff_line = faceoff_line.append(
        transform.reflect(faceoff_line, over_x=True, over_y=True))

    faceoff_line = faceoff_line.append(
        transform.reflect(faceoff_line, over_x=True, over_y=False))

    # Move the lines to be in the correct positions around the face-off spot
    # centers
    faceoff_line = transform.translate(faceoff_line,
                                       translate_x=center[0],
                                       translate_y=center[1])

    # If the spot is in the neutral zone, there should not be a circle around
    # it
    if abs(center[0]) == 20 or center == (0, 0):
        faceoff_line = pd.DataFrame({'x': [], 'y': []})

    # Rotate the coordinates if necessary
    if rotate:
        faceoff_line = transform.rotate(faceoff_line, rotation_dir)

    faceoff_line_dict = {'center': center, 'faceoff_lines': faceoff_line}

    return faceoff_line_dict
Exemple #3
0
def painted_area(full_surf=True, rotate=False, rotation_dir='ccw'):
    """
    Generate the dataframe for the points that comprise the bounding box of the
    free throw lane as specified in the court diagram of the WNBA rule book
    
    Parameters
    ----------
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature
    
    Returns
    -------
    painted_areas: a pandas dataframe of the painted areas
    """
    # The interior of the free throw lane is known as the painted area, and
    # can be a different color than the markings and court. These coordinates
    # can be used to color them on the plot
    painted_area = create.rectangle(x_min=-47,
                                    x_max=-28 - (2 / 12),
                                    y_min=-8 + (2 / 12),
                                    y_max=8 - (2 / 12))

    # Reflect the x coordinates over the y axis
    if full_surf:
        painted_area = painted_area.append(
            transform.reflect(painted_area, over_y=True))

    # Rotate the coordinates if necessary
    if rotate:
        painted_area = transform.rotate(painted_area, rotation_dir)

    return painted_area
Exemple #4
0
def net(full_surf=True, rotate=False, rotation_dir='ccw'):
    """
    Generate the dataframe for the points that comprise the rings as specified
    in the court diagram of the WNBA rule book
    
    Parameters
    ----------
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature

    Returns
    -------
    nets: a pandas dataframe of the nets
    """
    # The ring's center is 15" from the backboard, and 63" from the baseline,
    # which means it is centered at (+/-41.75, 0). The ring has an interior
    # diameter of 18", which is where the net is visible from above
    net = create.circle(center=(-41.75, 0), d=1.5)

    # Reflect the x coordinates over the y axis
    if full_surf:
        net = net.append(transform.reflect(net, over_y=True))

    # Rotate the coordinates if necessary
    if rotate:
        net = transform.rotate(net, rotation_dir)

    return net
Exemple #5
0
def division_line(full_surf=True, rotate=False, rotation_dir='ccw'):
    """
    Generate the dataframe for the points that comprise the bounding box of
    the division line as in the court diagram on page 8 of the WNBA rule book
    
    Parameters
    ----------
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature
    
    Returns
    -------
    division_line: A pandas dataframe of the division line on the court
    """
    # The line's center should be 47' from the interior side of the baselines,
    # and must be 2" thick
    division_line = create.rectangle(x_min=-1 / 12,
                                     x_max=0,
                                     y_min=-25,
                                     y_max=25)

    # Reflect the x coordinates over the y axis
    if full_surf:
        division_line = division_line.append(
            transform.reflect(division_line, over_y=True))

    # Rotate the coordinates if necessary
    if rotate:
        division_line = transform.rotate(division_line, rotation_dir)

    return division_line
Exemple #6
0
def blue_line(full_surf=True, rotate=False, rotation_dir='ccw'):
    """
    Generate the dataframe for the points that comprise the blue line as
    specified in Rule 1.7 of the NHL rule book
    
    Parameters
    ----------
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature
    
    Returns
    -------
    blue_line: a pandas dataframe of coordinates needed to plot the blue line
    """
    # The blue line is a 12" wide line that's 25' (interior) from the center
    # line. It spans the width of the playing surface
    blue_line = create.rectangle(x_min=-26, x_max=-25, y_min=-42.5, y_max=42.5)

    # Reflect the x coordinates over the y axis
    if full_surf:
        blue_line = blue_line.append(transform.reflect(blue_line, over_y=True))

    # Rotate the coordinates if necessary
    if rotate:
        blue_line = transform.rotate(blue_line, rotation_dir)

    return blue_line
Exemple #7
0
def try_line(full_surf=True, rotate=False, rotation_dir='ccw'):
    """
    Generate the dataframe for the points that comprise the bounding box of the
    try line as specified in the field diagram of the NCAA rule book (Appendix
    D)
    
    Parameters
    ----------
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature

    Returns
    -------
    try_line: a pandas dataframe of the goal line
    """
    try_line = create.rectangle(x_min=-141 - (2 / 12),
                                x_max=-141 + (2 / 12),
                                y_min=-1,
                                y_max=1)

    # Reflect the x coordinates over the y axis
    if full_surf:
        try_line = try_line.append(transform.reflect(try_line, over_y=True))

    # Rotate the coordinates if necessary
    if rotate:
        try_line = transform.rotate(try_line, rotation_dir)

    return try_line
Exemple #8
0
def backboard(full_surf=True, rotate=False, rotation_dir='ccw'):
    """
    Generate the dataframe for the points that comprise the backboard as
    specified in the court diagram of the WNBA rule book

    Returns
    -------
    backboard: a pandas dataframe of the backboard
    """
    # Per the rule book, the backboard must by 6' wide. The height of the
    # backboard is irrelevant in this graphic, as this is a bird's eye view
    # over the court
    backboard = create.rectangle(x_min=-43 - (4 / 12),
                                 x_max=-43,
                                 y_min=-3,
                                 y_max=3)

    # Reflect the x coordinates over the y axis
    if full_surf:
        backboard = backboard.append(transform.reflect(backboard, over_y=True))

    # Rotate the coordinates if necessary
    if rotate:
        backboard = transform.rotate(backboard, rotation_dir)

    return backboard
Exemple #9
0
def court_apron(full_surf = True, rotate = False, rotation_dir = 'ccw'):
    """
    Generate the dataframe for the points that comprise the bounding box of the
    court apron as in the court diagram on page 8 of the NBA rule book
    
    Parameters
    ----------
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature

    Returns
    -------
    court_apron: a pandas dataframe of the court border
    """
    # Courts have borders around the outside of the court that are different
    # in color than the end lines and sidelines. They are usually about 5' on
    # the sidelines and 8' on the end lines
    court_apron = pd.DataFrame({
        'x': [
            0,
            -47 - (2/12),
            -47 - (2/12),
            0,
            0,
            -55 - (4/12),
            -55 - (4/12),
            0,
            0
        ],
        
        'y': [
            -25 - (2/12),
            -25 - (2/12),
            25 + (2/12),
            25 + (2/12),
            30 + (2/12),
            30 + (2/12),
            -30 - (2/12),
            -30 - (2/12),
            -25 - (2/12)
        ]
    })
    
    # Reflect the x coordinates over the y axis
    if full_surf:
        court_apron = court_apron.append(
            transform.reflect(court_apron, over_y = True)
        )
    
    # Rotate the coordinates if necessary
    if rotate:
        court_apron = transform.rotate(
            court_apron,
            rotation_dir
        )
    
    return court_apron
Exemple #10
0
def boards(full_surf=True, rotate=False, rotation_dir='ccw'):
    """
    Generate the dataframe for the points that comprise the boards as specified
    in Rule 1.3 of the NHL rule book
    
    Parameters
    ----------
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature
    
    Returns
    -------
    boards: a pandas dataframe of coordinates needed to plot the boards
    """
    boards = pd.DataFrame({
        'x': [0],
        'y': [42.5]
    }).append(create.circle(center=(-72, 14.5), start=.5, end=1, d=56)).append(
        pd.DataFrame({
            'x': [-100],
            'y': [0]
        })).append(create.circle(
            center=(-72, -14.5), start=1, end=1.5, d=56)).append(
                pd.DataFrame({
                    'x': [0, 0],
                    'y': [-42.5, -42.5 - (2 / 12)]
                })).append(
                    create.circle(center=(-72, -14.5),
                                  start=1.5,
                                  end=1,
                                  d=56 + (4 / 12))).append(
                                      pd.DataFrame({
                                          'x': [-100 - (2 / 12)],
                                          'y': [0]
                                      })).append(
                                          create.circle(
                                              center=(-72, 14.5),
                                              start=1,
                                              end=.5,
                                              d=56 + (4 / 12))).append(
                                                  pd.DataFrame({
                                                      'x': [0, 0],
                                                      'y':
                                                      [42.5 + (2 / 12), 42.5]
                                                  }))

    # Reflect the x coordinates over the y axis
    if full_surf:
        boards = boards.append(transform.reflect(boards, over_y=True))

    # Rotate the coordinates if necessary
    if rotate:
        boards = transform.rotate(boards, rotation_dir)

    return boards
Exemple #11
0
def outer_center_circle(full_surf = True, rotate = False,
                        rotation_dir = 'ccw'):
    """
    Generate the dataframe for the points that comprise the outer center circle
    as in the court diagram on page 8 of the NBA rule book
    
    Parameters
    ----------
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature

    Returns
    -------
    outer_center_circle: A pandas dataframe containing the points that comprise
        the center circle of the court
    """
    # Draw the outer semicircle at half court. It has an outer radius of 6'
    # and thickness of 2"
    outer_center_circle = create.circle(
        d = 12 - (4/12),
        start = 1/2,
        end = 3/2
    ).append(
        pd.DataFrame({
            'x': [0],
            'y': [-12]
        })
    ).append(
        create.circle(
            d = 12,
            start = 3/2,
            end = 1/2
        )
    ).append(
        pd.DataFrame({
            'x': [0],
            'y': [12]
        })
    )
    
    # Reflect the x coordinates over the y axis
    if full_surf:
        outer_center_circle = outer_center_circle.append(
            transform.reflect(outer_center_circle, over_y = True)
        )
        
    # Rotate the coordinates if necessary
    if rotate:
        outer_center_circle = transform.rotate(
            outer_center_circle,
            rotation_dir
        )
    
    return outer_center_circle
Exemple #12
0
def goal(full_surf=True, rotate=False, rotation_dir='ccw'):
    """
    Generate the dataframe for the points that comprise the goals as specified
    in the court diagram of the WNBA rule book
    
    Parameters
    ----------
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature
    
    Returns
    -------
    goals: a pandas dataframe of the goals
    """
    # Get the starting angle of the ring. The connector has a width of 5", so
    # 2.5" are on either side. The ring has a radius of 9", so the arcsine of
    # these measurements should give the angle at which point they connect
    start_angle = np.pi - math.asin(2.5 / 9)

    # The ending angle of the ring would be the negative of the starting angle
    end_angle = -start_angle

    # Define the coordinates for the goal
    goal = pd.DataFrame({
        'x': [-43, -41.75 - ((9 / 12) * math.cos(start_angle))],
        'y': [2.5 / 12, 2.5 / 12]
    }).append(
        create.circle(
            center=(-41.75, 0),
            start=start_angle,
            end=end_angle,
            d=1.5 + (4 / 12))).append(
                pd.DataFrame({
                    'x':
                    [-41.75 - ((9 / 12) * math.cos(start_angle)), -43, -43],
                    'y': [-2.5 / 12, -2.5 / 12, 2.5 / 12]
                }))

    # Reflect the x coordinates over the y axis
    if full_surf:
        goal = goal.append(transform.reflect(goal, over_y=True))

    # Rotate the coordinates if necessary
    if rotate:
        goal = transform.rotate(goal, rotation_dir)

    return goal
Exemple #13
0
def endline_sideline(full_surf=True, rotate=False, rotation_dir='ccw'):
    """
    Generate the dataframe for the points that comprise the bounding box of the
    end lines, sidelines, hash marks, and substitution areas as in the court
    diagram on page 8 of the WNBA rule book, as well as the hash marks on the
    sides of the court
    
    Parameters
    ----------
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature

    Returns
    -------
    endline_sideline: a pandas dataframe of the end lines, side lines, hash
        marks, and substitution area markers
    """
    # The endline, sideline, hash marks, and substitution area form the
    # boundary of the court
    endline_sideline = pd.DataFrame({
        'x': [
            0, -19 + (2 / 12), -19 + (2 / 12), -19, -19, -47, -47,
            -47 + (6 / 12), -47 + (6 / 12), -47, -47, -47 + (6 / 12),
            -47 + (6 / 12), -47, -47, -19, -19, -19 + (2 / 12), -19 + (2 / 12),
            0, 0, -4, -4, -4 - (2 / 12), -4 - (2 / 12), -47 - (2 / 12),
            -47 - (2 / 12), 0, 0
        ],
        'y': [
            -25, -25, -22, -22, -25, -25, -11 - (2 / 12), -11 - (2 / 12), -11,
            -11, 11, 11, 11 + (2 / 12), 11 + (2 / 12), 25, 25, 22, 22, 25, 25,
            25 + (2 / 12), 25 + (2 / 12), 29, 29, 25 + (2 / 12), 25 + (2 / 12),
            -25 - (2 / 12), -25 - (2 / 12), -25
        ]
    })

    # Reflect the x coordinates over the y axis
    if full_surf:
        endline_sideline = endline_sideline.append(
            transform.reflect(endline_sideline, over_y=True))

    # Rotate the coordinates if necessary
    if rotate:
        endline_sideline = transform.rotate(endline_sideline, rotation_dir)

    return endline_sideline
Exemple #14
0
def goal_line(full_surf=True, rotate=False, rotation_dir='ccw'):
    """
    Generate the dataframe for the points that comprise the goal line as
    specified in Rule 1.7 of the NHL rule book
    
    Parameters
    ----------
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature
    
    Returns
    -------
    goal_line: a pandas dataframe of coordinates needed to plot the goal line
    """
    # The goal line is a little tricky. It is 11' away from the boards (or 89'
    # from the center), but follows the curvature of the boards in the corner.
    # To get the curvature, a similar calculation to that of the face-off spot
    # interior can be performed
    theta1 = math.asin((17 - (1 / 12)) / 28) / np.pi
    theta2 = math.asin((17 + (1 / 12)) / 28) / np.pi

    goal_line = create.circle(center=(-72, 14.5),
                              start=.5 + theta1,
                              end=.5 + theta2,
                              d=56).append(
                                  create.circle(center=(-72, -14.5),
                                                start=1.5 - theta2,
                                                end=1.5 - theta1,
                                                d=56)).append(
                                                    create.circle(
                                                        center=(-72, 14.5),
                                                        start=.5 + theta1,
                                                        end=.5 + theta2,
                                                        d=56).iloc[0])

    # Reflect the x coordinates over the y axis
    if full_surf:
        goal_line = goal_line.append(transform.reflect(goal_line, over_y=True))

    # Rotate the coordinates if necessary
    if rotate:
        goal_line = transform.rotate(goal_line, rotation_dir)

    return goal_line
Exemple #15
0
def referee_crease(full_surf=True, rotate=False, rotation_dir='ccw'):
    """
    Generate the dataframe for the points that comprise the referee's crease
    as specified in Rule 1.7 of the NHL rule book
    
    Parameters
    ----------
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature
    
    Returns
    -------
    referee_crease: a pandas dataframe of coordinates needed to plot the
        referee's crease
    """
    # The referee's crease is located at center ice in front of the penalty
    # timekeeper's seat. It is a 2" thick, 10' radius semi-circle
    referee_crease = create.circle(
        center=(0, -42.5), start=.5, end=1,
        d=20).append(pd.DataFrame({
            'x': [-10 + (2 / 12)],
            'y': [-42.5]
        })).append(
            create.circle(center=(0, -42.5), start=1, end=.5,
                          d=20 - (4 / 12))).append(
                              pd.DataFrame({
                                  'x': [0],
                                  'y': [10]
                              }))

    # Reflect the x coordinates over the y axis
    if full_surf:
        referee_crease = referee_crease.append(
            transform.reflect(referee_crease, over_y=True))

    # Rotate the coordinates if necessary
    if rotate:
        referee_crease = transform.rotate(referee_crease, rotation_dir)

    return referee_crease
Exemple #16
0
def goalkeeper_restricted_area(full_surf=True,
                               rotate=False,
                               rotation_dir='ccw'):
    """
    Generate the dataframe for the points that comprise the goalkeeper's
    restricted area as specified in Rule 1.8 of the NHL rule book
    
    Parameters
    ----------
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature
    
    Returns
    -------
    goalkeeper_restricted_area: a pandas dataframe of coordinates needed to
        plot the goalkeeper's restricted area
    """
    goalkeeper_restricted_area = pd.DataFrame({
        'x': [
            -100, -89 + 1 / 12, -89 + 1 / 12, -100, -100, -89 - (1 / 12),
            -89 - (1 / 12), -100, -100
        ],
        'y': [
            14, 11, -11, -14, -14 + (2 / 12), -11 + (2 / 12), 11 - (2 / 12),
            14 - (2 / 12), 14
        ]
    })

    # Reflect the x coordinates over the y axis
    if full_surf:
        goalkeeper_restricted_area = goalkeeper_restricted_area.append(
            transform.reflect(goalkeeper_restricted_area, over_y=True))

    # Rotate the coordinates if necessary
    if rotate:
        goalkeeper_restricted_area = transform.rotate(
            goalkeeper_restricted_area, rotation_dir)

    return goalkeeper_restricted_area
Exemple #17
0
def endline_sideline(full_surf=True, rotate=False, rotation_dir='ccw'):
    """
    Generate the dataframe for the points that comprise the bounding box of the
    end line and sideline as specified in the field diagram of the NCAA rule
    book (Appendix D)
    
    Parameters
    ----------
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature

    Returns
    -------
    endline_sideline: a pandas dataframe of the goal line
    """
    # The sidelines and end lines must be a solid white border, 6' wide along
    # the endlines, and 6' wide along the sidelines. The length of the field
    # is 100 yards, or 360' (interior on the inside of the goal lines), and
    # the sidelines connect with the end lines 10 yards (30') behind the
    # interior edge of the goal line. The field is 160' wide (interior)
    endline_sideline = pd.DataFrame({
        'x': [0, -180, -180, 0, 0, -186, -186, 0, 0],
        'y': [-80, -80, 80, 80, 86, 86, -86, -86, -80]
    })

    # Reflect the x coordinates over the y axis
    if full_surf:
        endline_sideline = endline_sideline.append(
            transform.reflect(endline_sideline, over_y=True))

    # Rotate the coordinates if necessary
    if rotate:
        endline_sideline = transform.rotate(endline_sideline, rotation_dir)

    return endline_sideline
Exemple #18
0
def goal_line(full_surf=True, rotate=False, rotation_dir='ccw'):
    """
    Generate the dataframe for the points that comprise the bounding box of the
    goal line as specified in the field diagram of the NCAA rule book(Appendix
    D)
    
    Parameters
    ----------
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature

    Returns
    -------
    goal_line: a pandas dataframe of the goal line
    """
    # The interior measurement between goal lines is 100 yards, or 300'. So,
    # taking the center of the field to be (0, 0), each goal line must be 150'
    # away from this point. Goal lines have thickness of 8", extending back
    # into the endzone, and extend across the width of the field (which totals
    # 160' across)
    goal_line = create.rectangle(x_min=-150 - (8 / 12),
                                 x_max=-150,
                                 y_min=-80,
                                 y_max=80)

    # Reflect the x coordinates over the y axis
    if full_surf:
        goal_line = goal_line.append(transform.reflect(goal_line, over_y=True))

    # Rotate the coordinates if necessary
    if rotate:
        goal_line = transform.rotate(goal_line, rotation_dir)

    return goal_line
Exemple #19
0
def free_throw_circle(full_surf=True, rotate=False, rotation_dir='ccw'):
    """
    Generate the dataframes for the points that comprise the free-throw circles
    as specified in page 8 of the WNBA rule book
    
    Parameters
    ----------
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature

    Returns
    -------
    free_throw_circles_dict: a dictionary with keys 'solid' and 'dash_x', where
        x is a number that corresponds to a dashed section of the free throw
        circle
    """
    # Per the WNBA rule book, the solid portion of the circle extends along an
    # arc of length 12.29" behind the free-throw line. The angle theta must be
    # calculated to determine where to start. It can be determined via the
    # relationship s = r*theta, where s is the arc length, r is the radius, and
    # theta is the angle (in radians)

    # First, define s to be the arc length in feet
    s = 12.29 / 12

    # The outer radius is 6'
    r = 6

    # Theta is therefore s/r, but since the create.circle() function takes an
    # angle in radians/pi, this must be divided out as well
    theta = (s / r) / np.pi

    # Since the circle must extend theta radians past +/-pi/2, theta must be
    # added/subtracted from 1/2 accordingly
    start_angle = (-1 / 2) - theta
    end_angle = (1 / 2) + theta

    # The free-throw circle is 6' in diameter from the center of the free-throw
    # line (exterior)
    free_throw_circle_solid = create.circle(
        center=(-28 - (1 / 12), 0), start=start_angle, end=end_angle,
        d=12).append(
            create.circle(center=(-28 - (1 / 12), 0),
                          start=end_angle,
                          end=start_angle,
                          d=12 - (4 / 12))).append(
                              create.circle(center=(-28 - (1 / 12), 0),
                                            start=start_angle,
                                            end=end_angle,
                                            d=12).iloc[0])

    # Reflect the x coordinates over the y axis
    if full_surf:
        free_throw_circle_solid = free_throw_circle_solid.append(
            transform.reflect(free_throw_circle_solid, over_y=True))

    # Rotate the coordinates if necessary
    if rotate:
        free_throw_circle_solid = transform.rotate(free_throw_circle_solid,
                                                   rotation_dir)

    # The dashed sections of the free-throw circle are all of length 15.5", and
    # are spaced 15.5" from each other. Following a similar process as above,
    # the starting and ending angles can be computed

    # First, compute the arc length in feet
    s = 15.5 / 12

    # The outer radius is 6'
    r = 6

    # Finally, compute the angle traced by the dashed lines
    theta_dashes = (s / r) / np.pi

    # This theta must be added to start_angle above to get the starting angle
    # for each dash, and added twice to get the ending angle for each dash.
    # This pattern can be repeated, taking the end angle of the previous dash
    # as the start angle for the following dash
    dash_1_start_angle = start_angle - theta_dashes
    dash_1_end_angle = start_angle - (2 * theta_dashes)

    dash_2_start_angle = dash_1_end_angle - theta_dashes
    dash_2_end_angle = dash_1_end_angle - (2 * theta_dashes)

    dash_3_start_angle = dash_2_end_angle - theta_dashes
    dash_3_end_angle = dash_2_end_angle - (2 * theta_dashes)

    free_throw_circle_dash_1 = create.circle(
        center=(-28 - (1 / 12), 0),
        start=dash_1_start_angle,
        end=dash_1_end_angle,
        d=12).append(
            create.circle(center=(-28 - (1 / 12), 0),
                          start=dash_1_end_angle,
                          end=dash_1_start_angle,
                          d=12 - (4 / 12))).append(
                              create.circle(center=(-28 - (1 / 12), 0),
                                            start=dash_1_start_angle,
                                            end=dash_1_end_angle,
                                            d=12).iloc[0])

    free_throw_circle_dash_2 = create.circle(
        center=(-28 - (1 / 12), 0),
        start=dash_2_start_angle,
        end=dash_2_end_angle,
        d=12).append(
            create.circle(center=(-28 - (1 / 12), 0),
                          start=dash_2_end_angle,
                          end=dash_2_start_angle,
                          d=12 - (4 / 12))).append(
                              create.circle(center=(-28 - (1 / 12), 0),
                                            start=dash_2_start_angle,
                                            end=dash_2_end_angle,
                                            d=12).iloc[0])

    free_throw_circle_dash_3 = create.circle(
        center=(-28 - (1 / 12), 0),
        start=dash_3_start_angle,
        end=dash_3_end_angle,
        d=12).append(
            create.circle(center=(-28 - (1 / 12), 0),
                          start=dash_3_end_angle,
                          end=dash_3_start_angle,
                          d=12 - (4 / 12))).append(
                              create.circle(center=(-28 - (1 / 12), 0),
                                            start=dash_3_start_angle,
                                            end=dash_3_end_angle,
                                            d=12).iloc[0])

    # The remaining dashes are just the reflections of dashes 1, 2, and 3 over
    # the x axis
    free_throw_circle_dash_4 = transform.reflect(free_throw_circle_dash_3,
                                                 over_x=True,
                                                 over_y=False)

    free_throw_circle_dash_5 = transform.reflect(free_throw_circle_dash_2,
                                                 over_x=True,
                                                 over_y=False)

    free_throw_circle_dash_6 = transform.reflect(free_throw_circle_dash_1,
                                                 over_x=True,
                                                 over_y=False)

    # Reflect the x coordinates over the y axis
    if full_surf:
        free_throw_circle_dash_1 = free_throw_circle_dash_1.append(
            transform.reflect(free_throw_circle_dash_1, over_y=True))

        free_throw_circle_dash_2 = free_throw_circle_dash_2.append(
            transform.reflect(free_throw_circle_dash_2, over_y=True))

        free_throw_circle_dash_3 = free_throw_circle_dash_3.append(
            transform.reflect(free_throw_circle_dash_3, over_y=True))

        free_throw_circle_dash_4 = free_throw_circle_dash_4.append(
            transform.reflect(free_throw_circle_dash_4, over_y=True))

        free_throw_circle_dash_5 = free_throw_circle_dash_5.append(
            transform.reflect(free_throw_circle_dash_5, over_y=True))

        free_throw_circle_dash_6 = free_throw_circle_dash_6.append(
            transform.reflect(free_throw_circle_dash_6, over_y=True))

    # Rotate the coordinates if necessary
    if rotate:
        free_throw_circle_dash_1 = transform.rotate(free_throw_circle_dash_1,
                                                    rotation_dir)

        free_throw_circle_dash_2 = transform.rotate(free_throw_circle_dash_2,
                                                    rotation_dir)

        free_throw_circle_dash_3 = transform.rotate(free_throw_circle_dash_3,
                                                    rotation_dir)

        free_throw_circle_dash_4 = transform.rotate(free_throw_circle_dash_4,
                                                    rotation_dir)

        free_throw_circle_dash_5 = transform.rotate(free_throw_circle_dash_5,
                                                    rotation_dir)

        free_throw_circle_dash_6 = transform.rotate(free_throw_circle_dash_6,
                                                    rotation_dir)

    free_throw_circle_dict = {
        'solid': free_throw_circle_solid,
        'dash_1': free_throw_circle_dash_1,
        'dash_2': free_throw_circle_dash_2,
        'dash_3': free_throw_circle_dash_3,
        'dash_4': free_throw_circle_dash_4,
        'dash_5': free_throw_circle_dash_5,
        'dash_6': free_throw_circle_dash_6
    }

    return free_throw_circle_dict
Exemple #20
0
def directional_arrows(full_surf=True, rotate=False, rotation_dir='ccw'):
    """
    Generate a dictionary for the points that comprise the directional arrrows
    that are located at every 10-yard interval as described in the NCAA rule
    book (Appendix D)
    
    Parameters
    ----------
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature

    Returns
    -------
    directional_arrows_dict: a dictionary of the directional arrows, and the
        coordinates required to plot them
    """
    # The arrow has two sides of 36", and one side of 18". The Pythagorean
    # Theorem can be used to determine the height (using half the length of
    # the base, which in this case is 18")
    arrow_width = np.sqrt(((36 / 12)**2) - ((9 / 12)**2))

    # The arrows only occur every 10 yards, but not at the 50 yard line
    arrow_yardages = np.arange(-40, 0, 10)

    # The output dictionary
    directional_arrows_dict = dict()

    # Create the arrows
    for yardage in arrow_yardages:
        arrow_lower = pd.DataFrame({
            'x': [
                # The numbers are 1' from the outer edge of the yard line,
                # which is 2" wide. The number itself is 4' wide, and the
                # number is 6" off the outside edge of the number
                (3 * yardage) - (2 / 12) - 5.5,
                (3 * yardage) - (2 / 12) - 5.5,
                (3 * yardage) - (2 / 12) - 5.5 - arrow_width,
                (3 * yardage) - (2 / 12) - 5.5
            ],
            'y': [
                # The bottom of the numbers must be 12 yards (36') off the
                # interior of the sideline. The number itself is then 6' tall,
                # and the top tip of the arrow is 15" below this line
                -53 - (15 / 12),
                -53 - (15 / 12) - (18 / 12),
                -53 - (15 / 12) - (9 / 12),
                -53 - (15 / 12)
            ]
        })

        arrow_upper = transform.reflect(arrow_lower, over_x=True, over_y=False)

        directional_arrows_dict[f'{50 + yardage}_arrow_l'] = arrow_lower
        directional_arrows_dict[f'{50 + yardage}_arrow_u'] = arrow_upper

    # Reflect the x coordinates over the y axis
    if full_surf:
        for directional_arrow, arrow_coords in directional_arrows_dict.items():
            directional_arrows_dict[directional_arrow] = pd.concat([
                arrow_coords,
                pd.DataFrame({
                    'x': -1 * arrow_coords['x'],
                    'y': arrow_coords['y']
                })
            ])

    # Rotate the coordinates if necessary
    if rotate:
        for directional_arrow, arrow_coords in directional_arrows_dict.items():
            directional_arrows_dict[directional_arrow] = transform.rotate(
                arrow_coords, rotation_dir)

    return directional_arrows_dict
Exemple #21
0
def yard_markings(full_surf=True, rotate=False, rotation_dir='ccw'):
    """
    Generate the dataframe for the points that comprise the bounding box of the
    line markings at 5-yard intervals as specified in the field diagram of the
    NCAA rule book (Appendix D)
    
    Parameters
    ----------
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature

    Returns
    -------
    yard_line_dict: a dictionary of the lines of the field, and the coordinates
        required to plot them
    """
    # The lines are to be placed 8" from the interior of the sidelines, and be
    # 4" thick. At 5-yard intervals across the field, the lines should stretch
    # the width of the field, with a 2' long by 4" wide hash 60' from the
    # interior of each boundary. At 1-yard intervals between these markings at
    # 5-yard intervals, a 2' tall by 4" wide marker should be placed 4" from
    # the interior of the sideline as well as 60' from the interior of the
    # sideline (and extending back towards the sideline). The field is being
    # constructed from left to right, and the right-side lines can be computed
    # via reflection, so only the left-side points and half the 50 yard line
    # are needed
    yardages = np.arange(-49, 1)

    yard_line_dict = dict()

    for yardage in yardages:
        if yardage == 0:
            # This is the 50 yard line. Only half the line is needed, since the
            # other half will be created via reflection
            yard_line = pd.DataFrame({
                'x': [
                    # Start 4" from the sideline
                    (3 * yardage) - (2 / 12),

                    # Lower inbound line marker
                    (3 * yardage) - (2 / 12),
                    (3 * yardage) - (2 / 12) - (10 / 12),
                    (3 * yardage) - (2 / 12) - (10 / 12),
                    (3 * yardage) - (2 / 12),

                    # Upper inbound line marker
                    (3 * yardage) - (2 / 12),
                    (3 * yardage) - (2 / 12) - (10 / 12),
                    (3 * yardage) - (2 / 12) - (10 / 12),
                    (3 * yardage) - (2 / 12),

                    # Top
                    (3 * yardage) - (2 / 12),

                    # Crossover
                    0,

                    # Return to bottom
                    0,

                    # Return to start
                    (3 * yardage) - (2 / 12)
                ],
                'y': [
                    # Start 4" from the sideline
                    -80 + (4 / 12),

                    # Lower inbound line marker
                    -20,
                    -20,
                    -20 + (4 / 12),
                    -20 + (4 / 12),

                    # Upper inbound line marker
                    20 - (4 / 12),
                    20 - (4 / 12),
                    20,
                    20,

                    # Top
                    80 - (8 / 12),

                    # Crossover
                    80 - (8 / 12),

                    # Return to bottom
                    -80 + (4 / 12),

                    # Return to start
                    -80 + (4 / 12),
                ]
            })

            yard_line_dict[f'yard_line_{50 + yardage}'] = yard_line

        elif yardage % 5 == 0:
            # At 5-yard intervals, the line should stretch the width of the
            # field, with the inbound line marker 60 from the interior of
            # the sideline boundary
            yard_line = pd.DataFrame({
                'x': [
                    # Start 4" from the sideline
                    (3 * yardage) - (2 / 12),

                    # Lower inbound line marker
                    (3 * yardage) - (2 / 12),
                    (3 * yardage) - (2 / 12) - (10 / 12),
                    (3 * yardage) - (2 / 12) - (10 / 12),
                    (3 * yardage) - (2 / 12),

                    # Upper inbound line marker
                    (3 * yardage) - (2 / 12),
                    (3 * yardage) - (2 / 12) - (10 / 12),
                    (3 * yardage) - (2 / 12) - (10 / 12),
                    (3 * yardage) - (2 / 12),

                    # Top
                    (3 * yardage) - (2 / 12),

                    # Crossover
                    (3 * yardage) + (2 / 12),

                    # Upper inbound line marker
                    (3 * yardage) + (2 / 12),
                    (3 * yardage) + (2 / 12) + (10 / 12),
                    (3 * yardage) + (2 / 12) + (10 / 12),
                    (3 * yardage) + (2 / 12),

                    # Lower inbound line marker
                    (3 * yardage) + (2 / 12),
                    (3 * yardage) + (2 / 12) + (10 / 12),
                    (3 * yardage) + (2 / 12) + (10 / 12),
                    (3 * yardage) + (2 / 12),

                    # Return to bottom
                    (3 * yardage) + (2 / 12),

                    # Return to start
                    (3 * yardage) - (2 / 12)
                ],
                'y': [
                    # Start 4" from the sideline
                    -80 + (4 / 12),

                    # Lower inbound line marker
                    -20,
                    -20,
                    -20 + (4 / 12),
                    -20 + (4 / 12),

                    # Upper inbound line marker
                    20 - (4 / 12),
                    20 - (4 / 12),
                    20,
                    20,

                    # Top
                    80 - (8 / 12),

                    # Crossover
                    80 - (8 / 12),

                    # Upper inbound line marker
                    20,
                    20,
                    20 - (4 / 12),
                    20 - (4 / 12),

                    # Lower inbound line marker
                    -20 + (4 / 12),
                    -20 + (4 / 12),
                    -20,
                    -20,

                    # Return to bottom
                    -80 + (8 / 12),

                    # Return to start
                    -80 + (4 / 12),
                ]
            })

            yard_line_dict[f'yard_line_{50 + yardage}'] = yard_line

        else:
            # At 1-yard intervals, the line should be 2' long. The line should
            # appear at the bottom (b) and top (t) of the field inside the 6'
            # wide boundary, and also appear at 70'9" from the nearest boundary
            # and extending from this point towards that boundary (l and u)
            yard_line_b = create.rectangle(x_min=(3 * yardage) - (2 / 12),
                                           x_max=(3 * yardage) + (2 / 12),
                                           y_min=-80 + (4 / 12),
                                           y_max=-78 + (4 / 12))

            yard_line_t = transform.reflect(yard_line_b,
                                            over_x=True,
                                            over_y=False)

            yard_line_l = create.rectangle(x_min=(3 * yardage) - (2 / 12),
                                           x_max=(3 * yardage) + (2 / 12),
                                           y_min=-22,
                                           y_max=-20)

            yard_line_u = transform.reflect(yard_line_l,
                                            over_x=True,
                                            over_y=False)

            yard_line_dict[f'yard_line_{50 + yardage}_b'] = yard_line_b
            yard_line_dict[f'yard_line_{50 + yardage}_t'] = yard_line_t
            yard_line_dict[f'yard_line_{50 + yardage}_l'] = yard_line_l
            yard_line_dict[f'yard_line_{50 + yardage}_u'] = yard_line_u

    # Reflect the x coordinates over the y axis
    if full_surf:
        for yard_line_no, yard_line_coords in yard_line_dict.items():
            yard_line_dict[yard_line_no] = pd.concat([
                yard_line_coords,
                pd.DataFrame({
                    'x': -1 * yard_line_coords['x'],
                    'y': yard_line_coords['y']
                })
            ])

    # Rotate the coordinates if necessary
    if rotate:
        for yard_line_no, yard_line_coords in yard_line_dict.items():
            yard_line_dict[yard_line_no] = transform.rotate(
                yard_line_coords, rotation_dir)

    return yard_line_dict
Exemple #22
0
def faceoff_circle(
        center=(0, 0), full_surf=True, rotate=False, rotation_dir='ccw'):
    """
    Generate the dataframe for the points that comprise the face-off circles
    as specified in Rule 1.9 of the NHL rule book
    
    Parameters
    ----------
    center: a tuple containing the center coordinates of the spot to be drawn
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature
    
    Returns
    -------
    faceoff_circle_dict: a dictionary with keys of 'center' and 'spot', where
        'center' corresponds to the center coordinates of the face-off spot,
        and 'spot' is a pandas dataframe of coordinates needed to plot a
        face-off circle
    """
    # The center circle has no external hash marks, so this circle just needs
    # to be a circle of 2" thickness
    if center == (0, 0):
        faceoff_circle = create.circle(
            center=(0, 0), start=.5, end=1.5, d=30).append(
                pd.DataFrame({
                    'x': [0, 0],
                    'y': [-15, -15 + (2 / 12)]
                })).append(
                    create.circle(center=(0, 0),
                                  start=1.5,
                                  end=.5,
                                  d=30 - (4 / 12))).append(
                                      pd.DataFrame({
                                          'x': [0, 0],
                                          'y': [15 - (2 / 12), 15]
                                      }))

    else:
        # Similar to the method described above, the starting angle to draw the
        # outer ring can be computed. The hash marks are 5' 11" (71") apart on
        # the exterior, so taking where this hash mark meets the circle to be
        # the center, the starting angle is computed as follows
        theta1 = math.asin((35.5 / 12) / 15) / np.pi

        # The same process gives the angle to find the point on the interior
        # of the hash mark, which are 5' 7" (67") apart
        theta2 = math.asin((33.5 / 12) / 15) / np.pi

        # Since the hash mark will be plotted on the top of the circle, the
        # starting angle will be theta + pi/2
        faceoff_circle = create.circle(
            center=(0, 0), start=.5 + theta1, end=1.5 - theta1, d=30).append(
                pd.DataFrame({
                    'x': [
                        -35.5 / 12,
                        -33.5 / 12,
                    ],
                    'y': [-17, -17]
                })).append(
                    create.circle(
                        center=(0, 0), start=1.5 - theta2, end=1.5,
                        d=30)).append(
                            pd.DataFrame({
                                'x': [0],
                                'y': [-15 + (2 / 12)]
                            })).append(
                                create.circle(
                                    center=(0, 0),
                                    start=1.5,
                                    end=.5,
                                    d=30 - (4 / 12))).append(
                                        pd.DataFrame({
                                            'x': [0],
                                            'y': [15]
                                        })).append(
                                            create.circle(
                                                center=(0, 0),
                                                start=.5,
                                                end=.5 + theta2,
                                                d=30)).append(
                                                    pd.DataFrame({
                                                        'x': [
                                                            -33.5 / 12,
                                                            -35.5 / 12,
                                                        ],
                                                        'y': [17, 17]
                                                    })).append(
                                                        create.circle(
                                                            center=(0, 0),
                                                            start=.5 + theta1,
                                                            end=1.5 - theta1,
                                                            d=30).iloc[0])

    # If the faceoff circle being drawn is the center circle, and only half
    # the ice is desired, return the semi-circle that is present
    if center == (0, 0) and not full_surf:
        pass

    # If the spot is in the neutral zone, there should not be a circle around
    # it
    elif abs(center[0]) == 20:
        faceoff_circle = pd.DataFrame({'x': [], 'y': []})

    else:
        # In all other cases, the entire circle should be generated, so the
        # current points set needs to be reflected over the y axis
        faceoff_circle = faceoff_circle.append(
            transform.reflect(faceoff_circle, over_y=True))

        faceoff_circle = transform.translate(faceoff_circle,
                                             translate_x=center[0],
                                             translate_y=center[1])

        # Rotate the coordinates if necessary. This is more for consistency
        # than necessity as a rotated circle is visually the same
        if rotate:
            faceoff_circle = transform.rotate(faceoff_circle, rotation_dir)

    faceoff_circle_dict = {'center': center, 'faceoff_circle': faceoff_circle}

    return faceoff_circle_dict
Exemple #23
0
def free_throw_lane(full_surf=True,
                    rotate=False,
                    rotation_dir='ccw',
                    include_amateur=False):
    """
    Generate the dataframe for the points that comprise the bounding box of the
    professional-sized free throw lane as in the court diagram on page 8 of
    the WNBA rule book. This free-throw lane is required for all teams. This
    will also generate the free-throw lane for amateur (college) basketball if
    desired
    
    Parameters
    ----------
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature

    Returns
    -------
    free_throw_lane_dict: a dictionary containing up to two pandas dataframes:
        one for professional-sized free-throw lanes, and one for amateur
        (college)
    """
    # The free throw lane goes from the endline inwards a distance of 18' 10"
    # (interior) and 19' (exterior) with a width of 6'
    pro_free_throw_lane = pd.DataFrame({
        'x':
        [-47, -28, -28, -47, -47, -28 - (2 / 12), -28 - (2 / 12), -47, -47],
        'y': [
            -8, -8, 8, 8, 8 - (2 / 12), 8 - (2 / 12), -8 + (2 / 12),
            -8 + (2 / 12), -8
        ]
    })

    # Reflect the x coordinates over the y axis
    if full_surf:
        pro_free_throw_lane = pro_free_throw_lane.append(
            transform.reflect(pro_free_throw_lane, over_y=True))

    # Rotate the coordinates if necessary
    if rotate:
        pro_free_throw_lane = transform.rotate(pro_free_throw_lane,
                                               rotation_dir)

    free_throw_lane_dict = {'professional': pro_free_throw_lane}

    if include_amateur:
        am_free_throw_lane = pd.DataFrame({
            'x': [
                -47, -28, -28, -47, -47, -28 - (2 / 12), -28 - (2 / 12), -47,
                -47
            ],
            'y': [
                -6, -6, 6, 6, 6 - (2 / 12), 6 - (2 / 12), -6 + (2 / 12),
                -6 + (2 / 12), -6
            ]
        })

        # Reflect the x coordinates over the y axis
        if full_surf:
            am_free_throw_lane = am_free_throw_lane.append(
                transform.reflect(am_free_throw_lane, over_y=True))

        # Rotate the coordinates if necessary
        if rotate:
            am_free_throw_lane = transform.rotate(am_free_throw_lane,
                                                  rotation_dir)

        free_throw_lane_dict['amateur'] = am_free_throw_lane

    return free_throw_lane_dict
Exemple #24
0
def goal_crease(full_surf=True, rotate=False, rotation_dir='ccw'):
    """
    Generate the dataframe for the points that comprise the goal crease outline
    and blue inner area as specified in Rule 1.7 of the NHL rule book
    
    Parameters
    ----------
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature
    
    Returns
    -------
    goal_crease_dict: a dictionary with keys of 'goal_crease_outline' and
        'goal_crease_inner', where 'goal_crease_outline' is a pandas dataframe
        that corresponds to the coordinates of the red outline of the goal
        crease, and 'goal_crease_inner' is a pandas dataframe of coordinates
        of the boundary of the blue interior of the goal crease
    """
    theta = math.asin(4 / 6) / np.pi

    goal_crease_outline = pd.DataFrame({
        'x': [-89 + (1 / 12), -89 + 4.5 + (1 / 12)],
        'y': [4, 4]
    }).append(create.circle(
        center=(-89, 0), start=theta, end=-theta, d=12)).append(
            pd.DataFrame({
                'x': [
                    -89 + (1 / 12), -89 + (1 / 12), -85 + (1 / 12),
                    -85 + (1 / 12), -85 + (3 / 12), -85 + (3 / 12)
                ],
                'y': [
                    -4, -4 + (2 / 12), -4 + (2 / 12), -4 + (7 / 12),
                    -4 + (7 / 12), -4 + (2 / 12)
                ]
            })).append(
                create.circle(center=(-89, 0),
                              start=-theta,
                              end=theta,
                              d=12 - (4 / 12))).append(
                                  pd.DataFrame({
                                      'x': [
                                          -85 + (3 / 12), -85 + (3 / 12),
                                          -85 + (1 / 12), -85 + (1 / 12),
                                          -89 + (1 / 12), -89 + (1 / 12)
                                      ],
                                      'y': [
                                          4 - (2 / 12), 4 - (7 / 12),
                                          4 - (7 / 12), 4 - (2 / 12),
                                          4 - (2 / 12), 4
                                      ]
                                  }))

    goal_crease_inner = pd.DataFrame({
        'x': [
            -89 + (1 / 12), -85 + (1 / 12), -85 + (1 / 12), -85 + (3 / 12),
            -85 + (3 / 12)
        ],
        'y': [
            -4 + (2 / 12), -4 + (2 / 12), -4 + (7 / 12), -4 + (7 / 12),
            -4 + (2 / 12)
        ]
    }).append(
        create.circle(center=(-89, 0),
                      start=-theta,
                      end=theta,
                      d=12 - (4 / 12))).append(
                          pd.DataFrame({
                              'x': [
                                  -85 + (3 / 12), -85 + (3 / 12),
                                  -85 + (1 / 12), -85 + (1 / 12),
                                  -89 + (1 / 12), -89 + (1 / 12)
                              ],
                              'y': [
                                  4 - (2 / 12), 4 - (7 / 12), 4 - (7 / 12),
                                  4 - (2 / 12), 4 - (2 / 12), -4 + (2 / 12)
                              ]
                          }))

    # Reflect the x coordinates over the y axis
    if full_surf:
        goal_crease_outline = goal_crease_outline.append(
            transform.reflect(goal_crease_outline, over_y=True))

        goal_crease_inner = goal_crease_inner.append(
            transform.reflect(goal_crease_inner, over_y=True))

    # Rotate the coordinates if necessary
    if rotate:
        goal_crease_outline = transform.rotate(goal_crease_outline,
                                               rotation_dir)

        goal_crease_inner = transform.rotate(goal_crease_inner, rotation_dir)

    goal_crease_dict = {
        'goal_crease_outline': goal_crease_outline,
        'goal_crease_inner': goal_crease_inner
    }

    return goal_crease_dict
Exemple #25
0
def three_point_line(full_surf=True, rotate=False, rotation_dir='ccw'):
    """
    Generate the dataframe for the points that comprise the three-point line
    as specified in the WNBA rule book
    
    Parameters
    ----------
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature
    
    Returns
    -------
    three_point_lines: a pandas dataframe of the three-point line
    """
    # First, a bit of math is needed to determine the starting and ending
    # angles of the three-point arc, relative to 0 radians. Since in the end,
    # the angle is what matters, the units of measure do not. Inches are easier
    # to use for this calculation. The angle begins 3' from the interior edge
    # of the sideline
    start_y = (22 * 12)

    # The rule book describes the arc as having a radius of 23' 9"
    radius_outer = (22 * 12) + 1.75

    # From here, the calculation is relatively straightforward. To determine
    # the angle, the inverse sine is needed. It will be multiplied by pi
    # so that it can be passed to the create_circle() function
    start_angle_outer = math.asin(start_y / radius_outer) / np.pi
    end_angle_outer = -start_angle_outer

    # The same method can be used for the inner angles, however, since the
    # inner radius will be traced from bottom to top, the angle must be
    # negative to start
    radius_inner = (22 * 12) + 1.75 - 2
    start_angle_inner = -math.asin((start_y - 2) / radius_inner) / np.pi
    end_angle_inner = -start_angle_inner

    # According to the rulebook, the three-point line is 21' 7 7/8" in the
    # corners
    three_point_line = pd.DataFrame({
        'x': [-47],
        'y': [22]
    }).append(
        create.circle(center=(-41.75, 0),
                      d=2 * (radius_outer / 12),
                      start=start_angle_outer,
                      end=end_angle_outer)).append(
                          pd.DataFrame({
                              'x': [-47, -47],
                              'y': [-22, -22 + (2 / 12)]
                          })).append(
                              create.circle(center=(-41.75, 0),
                                            d=2 * (radius_inner / 12),
                                            start=start_angle_inner,
                                            end=end_angle_inner)).append(
                                                pd.DataFrame({
                                                    'x': [-47, -47],
                                                    'y': [22 - (2 / 12), 22]
                                                }))

    # Reflect the x coordinates over the y axis
    if full_surf:
        three_point_line = three_point_line.append(
            transform.reflect(three_point_line, over_y=True))

    # Rotate the coordinates if necessary
    if rotate:
        three_point_line = transform.rotate(rotation_dir, three_point_line)

    return three_point_line
Exemple #26
0
def restricted_area_arc(full_surf=True, rotate=False, rotation_dir='ccw'):
    """
    Generate the dataframe for the points that comprise the restricted-area
    arcs as specified in the WNBA rule book
    
    Parameters
    ----------
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature

    Returns
    -------
    restricted_area_arcs: a pandas dataframe of the restricted-area arcs
    """
    # Following the same process as for the three-point line, the restricted
    # area arc's starting and ending angle can be computed
    start_y = -4 - (2 / 12)

    # The rule book describes the arc as having a radius of 4'
    radius_outer = 4 + (2 / 12)

    # From here, the calculation is relatively straightforward. To determine
    # the angle, the inverse sine is needed. It will be multiplied by pi
    # so that it can be passed to the create_circle() function
    start_angle_outer = math.asin(start_y / radius_outer) / np.pi
    end_angle_outer = -start_angle_outer

    # The same method can be used for the inner angles, however, since the
    # inner radius will be traced from bottom to top, the angle must be
    # negative to start
    radius_inner = 4
    start_angle_inner = -math.asin((start_y + (2 / 12)) / radius_inner) / np.pi
    end_angle_inner = -start_angle_inner

    # The restricted area arc is an arc of radius 4' from the center of the
    # basket, and extending in a straight line to the front face of the
    # backboard, and having thickness of 2"
    restricted_area_arc = pd.DataFrame({
        'x': [-43],
        'y': [-4 - (2 / 12)]
    }).append(
        create.circle(center=(-41.75, 0),
                      d=8 + (4 / 12),
                      start=start_angle_outer,
                      end=end_angle_outer)).append(
                          pd.DataFrame({
                              'x': [-43, -43],
                              'y': [4 + (2 / 12), 4]
                          })).append(
                              create.circle(center=(-41.75, 0),
                                            d=8,
                                            start=start_angle_inner,
                                            end=end_angle_inner)).append(
                                                pd.DataFrame({
                                                    'x': [-43, -43],
                                                    'y': [-4, -4 - (2 / 12)]
                                                }))

    # Reflect the x coordinates over the y axis
    if full_surf:
        restricted_area_arc = restricted_area_arc.append(
            transform.reflect(restricted_area_arc, over_y=True))

    # Rotate the coordinates if necessary
    if rotate:
        restricted_area_arc = transform.rotate(rotation_dir,
                                               restricted_area_arc)

    return restricted_area_arc
Exemple #27
0
def faceoff_spot(
        center=(0, 0), full_surf=True, rotate=False, rotation_dir='ccw'):
    """
    Generate the dataframe for the points that comprise the face-off spots as
    specified in Rule 1.9 of the NHL rule book
    
    Parameters
    ----------
    center: a tuple containing the center coordinates of the spot to be drawn
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature
    
    Returns
    -------
    spot_dict: a dictionary with keys of 'center' and 'spot', where 'center'
        corresponds to the center coordinates of a face-off spot, and 'spot'
        is a pandas dataframe of coordinates to plot to make a face-off spot
    """
    # Center faceoff spot
    if center == (0, 0):
        # The center face-off spot is 12" in diameter
        spot = create.circle(center=(0, 0), start=1 / 2, end=3 / 2, d=1)

        # Reflect the x coordinates over the y axis
        if full_surf:
            spot = create.circle(center=(0, 0), start=1 / 2, end=5 / 2, d=1)

        # Rotate the coordinates if necessary
        if rotate:
            spot = transform.rotate(spot, rotation_dir)

        spot_dict = {
            'center': center,
            'spot_outer': spot,
            'spot_inner': pd.DataFrame({
                'x': [],
                'y': []
            })
        }

        return spot_dict

    else:
        # The non-center face-off spots are 2' in diameter, with a 3" gap
        # between the top and bottom of the spot and the strip in the center.
        # First, find the angle at which to start the trace for the interior
        # of the spot.

        # The spot has a radius of 1', and a thickness of 2", so the inner
        # radius is 10". Since there is a 3" gap at theta = 180deg, this
        # indicates that the stripe's curve starts at x = -7" from the center.
        # Using trigonometry, the angle can be computed
        theta = math.asin(7 / 10) / np.pi

        # To draw this evenly, it's easiest to create two dataframes: one is
        # the underlying red spot of diameter 2', and the other being the
        # white inner portion described in the rule book. The starting point
        # is found using theta calculated above
        spot_outer = create.circle(center=(0, 0), start=0, end=2, d=2)

        spot_inner = create.circle(center=(0, 0),
                                   start=.5 + theta,
                                   end=1.5 - theta,
                                   d=2 - (4 / 12)).append(
                                       create.circle(center=(0, 0),
                                                     start=.5 + theta,
                                                     end=1.5 - theta,
                                                     d=2 - (4 / 12)).iloc[0])

        spot_inner = spot_inner.append(
            transform.reflect(spot_inner, over_y=True))

        # Rotate the coordinates if necessary
        if rotate:
            spot_outer = transform.rotate(spot_outer, rotation_dir)

            spot_inner = transform.rotate(spot_inner, rotation_dir)

        spot_outer = transform.translate(spot_outer,
                                         translate_x=center[0],
                                         translate_y=center[1])

        spot_inner = transform.translate(spot_inner,
                                         translate_x=center[0],
                                         translate_y=center[1])

        spot_dict = {
            'center': center,
            'spot_outer': spot_outer,
            'spot_inner': spot_inner
        }

        return spot_dict
Exemple #28
0
def blocks(full_surf=True,
           rotate=False,
           rotation_dir='ccw',
           include_amateur=False):
    """
    Generate the dataframes for the points that comprise the blocks as
    specified in page 8 of the WNBA rule book
    
    Parameters
    ----------
    full_surf: a bool indicating whether or not this feature is needed for a
        full-surface representation
    rotate: a bool indicating whether or not this feature needs to be rotated
    rotation_dir: a string indicating which direction to rotate the feature

    Returns
    -------
    blocks_dict: a dictionary with the block numbers as keys and the dataframes
        to use for plotting as values
    """
    # The first block is 7' (interior) from the endline, and is 2" thick
    professional_block_1 = create.rectangle(x_min=-40,
                                            x_max=-40 + (2 / 12),
                                            y_min=-8.5,
                                            y_max=-8)

    # The second block is 8" (interior) from the first block, and is 2" thick
    professional_block_2 = create.rectangle(x_min=-40 + (10 / 12),
                                            x_max=-39,
                                            y_min=-8.5,
                                            y_max=-8)

    # The third block is 3' (interior) from the second block, and is 2" thick
    professional_block_3 = create.rectangle(x_min=-36,
                                            x_max=-36 + (2 / 12),
                                            y_min=-8.5,
                                            y_max=-8)

    # The fourth block is 3' (interior) from the third block, and is 2" thick
    professional_block_4 = create.rectangle(x_min=-33 + (2 / 12),
                                            x_max=-33 + (4 / 12),
                                            y_min=-8.5,
                                            y_max=-8)

    # Block 1 but on the other side of the free-throw lane
    professional_block_5 = create.rectangle(x_min=-40,
                                            x_max=-40 + (2 / 12),
                                            y_min=8.5,
                                            y_max=8)

    # Block 2 but on the other side of the free-throw lane
    professional_block_6 = create.rectangle(x_min=-40 + (10 / 12),
                                            x_max=-39,
                                            y_min=8.5,
                                            y_max=8)

    # Block 3 but on the other side of the free-throw lane
    professional_block_7 = create.rectangle(x_min=-36,
                                            x_max=-36 + (2 / 12),
                                            y_min=8.5,
                                            y_max=8)

    # Block 4 but on the other side of the free-throw lane
    professional_block_8 = create.rectangle(x_min=-33 + (2 / 12),
                                            x_max=-33 + (4 / 12),
                                            y_min=8.5,
                                            y_max=8)

    # Reflect the x coordinates over the y axis
    if full_surf:
        professional_block_1 = professional_block_1.append(
            transform.reflect(professional_block_1, over_y=True))

        professional_block_2 = professional_block_2.append(
            transform.reflect(professional_block_2, over_y=True))

        professional_block_3 = professional_block_3.append(
            transform.reflect(professional_block_3, over_y=True))

        professional_block_4 = professional_block_4.append(
            transform.reflect(professional_block_4, over_y=True))

        professional_block_5 = professional_block_5.append(
            transform.reflect(professional_block_5, over_y=True))

        professional_block_6 = professional_block_6.append(
            transform.reflect(professional_block_6, over_y=True))

        professional_block_7 = professional_block_7.append(
            transform.reflect(professional_block_7, over_y=True))

        professional_block_8 = professional_block_8.append(
            transform.reflect(professional_block_8, over_y=True))

    # Rotate the coordinates if necessary
    if rotate:
        professional_block_1 = transform.rotate(professional_block_1,
                                                rotation_dir)

        professional_block_2 = transform.rotate(professional_block_2,
                                                rotation_dir)

        professional_block_3 = transform.rotate(professional_block_3,
                                                rotation_dir)

        professional_block_4 = transform.rotate(professional_block_4,
                                                rotation_dir)

        professional_block_5 = transform.rotate(professional_block_5,
                                                rotation_dir)

        professional_block_6 = transform.rotate(professional_block_6,
                                                rotation_dir)

        professional_block_7 = transform.rotate(professional_block_7,
                                                rotation_dir)

        professional_block_8 = transform.rotate(professional_block_8,
                                                rotation_dir)

    blocks_dict = {
        'professional_block_1': professional_block_1,
        'professional_block_2': professional_block_2,
        'professional_block_3': professional_block_3,
        'professional_block_4': professional_block_4,
        'professional_block_5': professional_block_5,
        'professional_block_6': professional_block_6,
        'professional_block_7': professional_block_7,
        'professional_block_8': professional_block_8,
    }

    if include_amateur:
        # The first set of blocks are 7' from the interior of the baseline, and
        # measure 1' in width
        amateur_block_1 = create.rectangle(x_min=-40,
                                           x_max=-39,
                                           y_min=-6 - (8 / 12),
                                           y_max=-6)

        # The second set of blocks are 3' from the first block, and measure 2"
        # in width
        amateur_block_2 = create.rectangle(x_min=-36,
                                           x_max=-36 + (2 / 12),
                                           y_min=-6 - (8 / 12),
                                           y_max=-6)

        # The third set of blocks are 3' from the second block, and measure 2"
        # in width
        amateur_block_3 = create.rectangle(x_min=-33 + (2 / 12),
                                           x_max=-33 + (4 / 12),
                                           y_min=-6 - (8 / 12),
                                           y_max=-6)

        # The fourth set of blocks are 3' from the third block, and measure 2"
        # in width
        amateur_block_4 = create.rectangle(x_min=-30 + (4 / 12),
                                           x_max=-30 + (6 / 12),
                                           y_min=-6 - (8 / 12),
                                           y_max=-6)

        # Block 1 but on the other side of the free-throw lane
        amateur_block_5 = create.rectangle(x_min=-40,
                                           x_max=-39,
                                           y_min=6,
                                           y_max=6 + (8 / 12))

        # Block 2 but on the other side of the free-throw lane
        amateur_block_6 = create.rectangle(x_min=-36,
                                           x_max=-36 + (2 / 12),
                                           y_min=6,
                                           y_max=6 + (8 / 12))

        # Block 3 but on the other side of the free-throw lane
        amateur_block_7 = create.rectangle(x_min=-33 + (2 / 12),
                                           x_max=-33 + (4 / 12),
                                           y_min=6,
                                           y_max=6 + (8 / 12))

        # Block 4 but on the other side of the free-throw lane
        amateur_block_8 = create.rectangle(x_min=-30 + (4 / 12),
                                           x_max=-30 + (6 / 12),
                                           y_min=6,
                                           y_max=6 + (8 / 12))

        # Reflect the x coordinates over the y axis
        if full_surf:
            amateur_block_1 = amateur_block_1.append(
                transform.reflect(amateur_block_1, over_y=True))

            amateur_block_2 = amateur_block_2.append(
                transform.reflect(amateur_block_2, over_y=True))

            amateur_block_3 = amateur_block_3.append(
                transform.reflect(amateur_block_3, over_y=True))

            amateur_block_4 = amateur_block_4.append(
                transform.reflect(amateur_block_4, over_y=True))

            amateur_block_5 = amateur_block_5.append(
                transform.reflect(amateur_block_5, over_y=True))

            amateur_block_6 = amateur_block_6.append(
                transform.reflect(amateur_block_6, over_y=True))

            amateur_block_7 = amateur_block_7.append(
                transform.reflect(amateur_block_7, over_y=True))

            amateur_block_8 = amateur_block_8.append(
                transform.reflect(amateur_block_8, over_y=True))

        # Rotate the coordinates if necessary
        if rotate:
            amateur_block_1 = transform.rotate(amateur_block_1, rotation_dir)

            amateur_block_2 = transform.rotate(amateur_block_2, rotation_dir)

            amateur_block_3 = transform.rotate(amateur_block_3, rotation_dir)

            amateur_block_4 = transform.rotate(amateur_block_4, rotation_dir)

            amateur_block_5 = transform.rotate(amateur_block_5, rotation_dir)

            amateur_block_6 = transform.rotate(amateur_block_6, rotation_dir)

            amateur_block_7 = transform.rotate(amateur_block_7, rotation_dir)

            amateur_block_8 = transform.rotate(amateur_block_8, rotation_dir)

        blocks_dict['amateur_block_1'] = amateur_block_1
        blocks_dict['amateur_block_2'] = amateur_block_2
        blocks_dict['amateur_block_3'] = amateur_block_3
        blocks_dict['amateur_block_4'] = amateur_block_4
        blocks_dict['amateur_block_5'] = amateur_block_5
        blocks_dict['amateur_block_6'] = amateur_block_6
        blocks_dict['amateur_block_7'] = amateur_block_7
        blocks_dict['amateur_block_8'] = amateur_block_8

    return blocks_dict