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
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
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
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
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
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
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
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
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