-
Notifications
You must be signed in to change notification settings - Fork 82
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Plot arcs method #84
base: main
Are you sure you want to change the base?
Plot arcs method #84
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
""" | ||
========================== | ||
Plotting arcs around goals | ||
========================== | ||
|
||
This example shows how to plot arcs around goals at specified distances on various pitch types. | ||
""" | ||
from mplsoccer import Pitch, VerticalPitch | ||
import matplotlib.pyplot as plt | ||
|
||
|
||
def plot_arcs_around_goals_on_pitches(pitch_cls): | ||
fig, axes = plt.subplots(4, 2, figsize=(12, 14)) | ||
axes = axes.ravel() | ||
pitch_types = ['statsbomb', 'opta', 'tracab', 'skillcorner', 'wyscout', | ||
'metricasports', 'uefa', 'custom'] | ||
|
||
placements = ('top', 'bottom') if pitch_cls == VerticalPitch else ('left', 'right') | ||
colors = ('red', 'blue') | ||
for idx, pt in enumerate(pitch_types): | ||
if pt in ['tracab', 'metricasports', 'custom', 'skillcorner']: | ||
pitch = pitch_cls(pitch_type=pt, pitch_length=105, pitch_width=68) | ||
else: | ||
pitch = pitch_cls(pitch_type=pt) | ||
|
||
pitch.draw(axes[idx]) | ||
axes[idx].set_title(pt, fontsize=20, c='black', pad=15) | ||
for radius_meters in (5, 10, 20, 30): | ||
for color, placement in zip(colors, placements): | ||
pitch.add_arc_around_goal(axes[idx], radius_meters, placement=placement, color=color) | ||
return fig, axes | ||
|
||
|
||
############################################################################## | ||
# Horizontal pitches | ||
|
||
plot_arcs_around_goals_on_pitches(Pitch) | ||
|
||
############################################################################## | ||
# Vertical pitches | ||
|
||
plot_arcs_around_goals_on_pitches(VerticalPitch) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -114,6 +114,70 @@ def _reverse_vertices_if_vertical(vert): | |
def _reverse_annotate_if_vertical(annotate): | ||
return annotate | ||
|
||
def add_arc_around_goal(self, ax, radius_meters, placement='left', **kwargs): | ||
"""Add an arc to the pitch centred on one of the goals. | ||
|
||
Parameters | ||
---------- | ||
ax : matplotlib axis | ||
A matplotlib.axes.Axes to draw the arc on. | ||
radius_meters : float | ||
The radius of the arc in meters. | ||
placement : str, default 'left' | ||
Which side of the pitch to plot the arc. Should be either 'left' or 'right'. | ||
kwargs : dict | ||
Additional keyword arguments accepted by matplotlib.patches.Arc. | ||
|
||
Returns | ||
------- | ||
Arc added to `ax` and None returned. | ||
|
||
Raises | ||
------ | ||
ValueError | ||
If `placement` is not 'left' or 'right'. | ||
|
||
Examples | ||
-------- | ||
>>> from mplsoccer import Pitch | ||
>>> pitch = Pitch() | ||
>>> fig, ax = pitch.draw() | ||
>>> pitch.add_arc_around_goal(ax, radius_meters=10, placement="left") | ||
>>> pitch.add_arc_around_goal(ax, radius_meters=20, placement="right", color="red") | ||
""" | ||
xmin, xmax, ymin, ymax = self.extent | ||
length_pixels = abs(xmax - xmin) | ||
width_pixels = abs(ymax - ymin) | ||
|
||
# scale down via ratio of radius to dimension | ||
if self.pitch_type != 'metricasports': | ||
width = 2 * length_pixels * radius_meters / self.dim.length | ||
height = 2 * width_pixels * radius_meters / self.dim.width / self.dim.aspect | ||
if self.pitch_type == 'tracab': | ||
# has unit centimeters | ||
width *= 100 | ||
height *= 100 | ||
elif self.pitch_type == 'metricasports': | ||
width = 2 * length_pixels * radius_meters / self.dim.pitch_length | ||
height = 2 * width_pixels * radius_meters / self.dim.pitch_width | ||
|
||
if self.dim.origin_center: | ||
y = 0 | ||
else: | ||
y = width_pixels / 2 - self.pad_bottom | ||
if placement == 'left': | ||
self._draw_arc( | ||
ax, xmin + self.pad_left, y, width, height, theta1=-90, theta2=90, **kwargs | ||
) | ||
elif placement == 'right': | ||
self._draw_arc( | ||
ax, xmax - self.pad_right, y, width, height, theta1=90, theta2=270, **kwargs | ||
) | ||
else: | ||
raise ValueError( | ||
f'placement={placement} should be "left" or "right"' | ||
) | ||
|
||
|
||
class VerticalPitch(BasePitchPlot): | ||
|
||
|
@@ -222,3 +286,67 @@ def _reverse_vertices_if_vertical(vert): | |
@staticmethod | ||
def _reverse_annotate_if_vertical(annotate): | ||
return annotate[::-1] | ||
|
||
def add_arc_around_goal(self, ax, radius_meters, placement, **kwargs): | ||
"""Add an arc to the pitch centred on one of the goals. | ||
|
||
Parameters | ||
---------- | ||
ax : matplotlib axis | ||
A matplotlib.axes.Axes to draw the arc on. | ||
radius_meters : float | ||
The radius of the arc in meters. | ||
placement : str, default 'top' | ||
Which side of the pitch to plot the arc. Should be either 'top' or 'bottom'. | ||
kwargs : dict | ||
Additional keyword arguments accepted by matplotlib.patches.Arc. | ||
|
||
Returns | ||
------- | ||
Arc added to `ax` and None returned. | ||
|
||
Raises | ||
------ | ||
ValueError | ||
If `placement` is not 'top' or 'bottom'. | ||
|
||
Examples | ||
-------- | ||
>>> from mplsoccer import Pitch | ||
>>> pitch = Pitch() | ||
>>> fig, ax = pitch.draw() | ||
>>> pitch.add_arc_around_goal(ax, radius_meters=15, placement="bottom") | ||
>>> pitch.add_arc_around_goal(ax, radius_meters=7, placement="top", color="blue") | ||
""" | ||
xmin, xmax, ymin, ymax = self.extent | ||
width_pixels = abs(xmax - xmin) | ||
length_pixels = abs(ymax - ymin) | ||
|
||
# scale down via ratio of radius to dimension | ||
if self.pitch_type != "metricasports": | ||
width = 2 * width_pixels * self.dim.aspect * radius_meters / self.dim.width | ||
height = 2 * length_pixels * radius_meters / self.dim.length | ||
if self.pitch_type == "tracab": | ||
# has unit centimeters | ||
width *= 100 | ||
height *= 100 | ||
elif self.pitch_type == "metricasports": | ||
width = 2 * width_pixels * radius_meters / self.dim.pitch_width | ||
height = 2 * length_pixels * radius_meters / self.dim.pitch_length | ||
|
||
if self.dim.origin_center: | ||
x = 0 | ||
else: | ||
x = width_pixels / 2 - self.pad_left | ||
if placement == 'bottom': | ||
self._draw_arc( | ||
ax, ymin + self.pad_bottom, x, height, width, theta1=-90, theta2=90, **kwargs | ||
) | ||
Comment on lines
+342
to
+344
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
elif placement == 'top': | ||
self._draw_arc( | ||
ax, ymax - self.pad_top, x, height, width, theta1=90, theta2=270, **kwargs | ||
) | ||
else: | ||
raise ValueError( | ||
f'placement={placement} should be "top" or "bottom"' | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a simplified version of
examples/pitch_setup/plot_compare_pitches.py
which I was using to make sure I dealt with the subtleties of the different coordinate systems and aspect ratios.